]> Git Repo - VerusCoin.git/blame - src/cc/dice.cpp
Cmpflag
[VerusCoin.git] / src / cc / dice.cpp
CommitLineData
cfea7a46 1/******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
3 * *
4 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
5 * the top-level directory of this distribution for the individual copyright *
6 * holder information and the developer policies on copyright and licensing. *
7 * *
8 * Unless otherwise agreed in a custom licensing agreement, no part of the *
9 * SuperNET software, including this file may be copied, modified, propagated *
10 * or distributed except according to the terms contained in the LICENSE file *
11 * *
12 * Removal or modification of this copyright notice is prohibited. *
13 * *
14 ******************************************************************************/
15
16#include "CCdice.h"
cfea7a46 17
237dae63 18// hentropy_proof, timeout, validate, autoqueue, compare address to match house
afef48c0 19
cfea7a46 20/*
10078e85 21 in order to implement a dice game, we need a source of entropy, reasonably fast completion time and a way to manage the utxos.
22
9025093e 23 1. CC vout locks "house" funds with hash(entropy)
10078e85 24 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout.
9025093e 25 3. house account sends funds to winner/loser with proof of entropy
26 4. if timeout, bettor gets refund
10078e85 27
28 2. and 3. can be done in mempool
9025093e 29
30 The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction.
31
753a6f92 32 To bet, one of these 'E' transactions is used as the first input and its hashed entropy is combined with the unhashed entropy attached to the bet 'B' transaction.
33
34 The house node monitors the 'B' transactions and if it sees one of its own, it creates either a winner 'W' or loser 'L' transaction, with proof of hash of entropy.
35
36 In the even the house node doesnt respond before timeoutblocks, then anybody (including bettor) can undo the bet with funds going back to the house and bettor
7eb60411 37
38 In order for people to play dice, someone (anyone) needs to create a funded dice plan and addfunding with enough utxo to allow players to find one.
39
40createfunding:
41 vins.*: normal inputs
42 vout.0: CC vout for funding
32e3be87 43 vout.1: owner vout
44 vout.2: dice marker address vout for easy searching
45 vout.3: normal change
7eb60411 46 vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
47
48addfunding (entropy):
49 vins.*: normal inputs
50 vout.0: CC vout for locked entropy funds
51 vout.1: tag to owner address for entropy funds
52 vout.2: normal change
53 vout.n-1: opreturn 'E' sbits fundingtxid hentropy
54
55bet:
56 vin.0: entropy txid from house
57 vins.1+: normal inputs
58 vout.0: CC vout for locked entropy
59 vout.1: CC vout for locked bet
60 vout.2: tag for bettor's address (txfee + odds)
61 vout.3: change
62 vout.n-1: opreturn 'B' sbits fundingtxid entropy
63
64winner:
65 vin.0: betTx CC vout.0 entropy from bet
66 vin.1: betTx CC vout.1 bet amount from bet
02409974 67 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 68 vout.0: funding CC change to entropy owner
69 vout.1: tag to owner address for entropy funds
70 vout.2: normal output to bettor's address
71 vout.n-1: opreturn 'W' sbits fundingtxid hentropy
72
73loser:
74 vin.0: betTx CC vout.0 entropy from bet
75 vin.1: betTx CC vout.1 bet amount from bet
02409974 76 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 77 vout.0: funding CC to entropy owner
78 vout.1: tag to owner address for entropy funds
79 vout.n-1: opreturn 'L' sbits fundingtxid hentropy
80
02409974 81timeout:
7eb60411 82 vin.0: betTx CC vout.0 entropy from bet
83 vin.1: betTx CC vout.1 bet amount from bet
02409974 84 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 85 vout.0: funding CC vin.0 to entropy owner
86 vout.1: tag to owner address for entropy funds
87 vout.2: normal vin.1 to bettor's address
02409974 88 vout.n-1: opreturn 'T' sbits fundingtxid hentropy
7eb60411 89
90
10078e85 91 */
cfea7a46 92
ae748eaf 93#include "../endian.h"
94
95void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
96{
97 int32_t i,j=0;
98#if defined(WORDS_BIGENDIAN)
99 for (i=31; i>=0; i--)
100 dest[j++] = src[i];
101#else
17bc6201 102 memcpy(dest,src,len);
ae748eaf 103#endif
104}
105
dfe4c146 106uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
8138fad2 107{
ae748eaf 108 int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
72362bbe 109 memset(&hentropy,0,32);
e0e655fe 110 endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
d176d2df 111 txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
112 txidpub = curve25519(txidpriv,curve25519_basepoint9());
113
114 Myprivkey(tmp256.bytes);
115 vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
116 mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
117 mypub = curve25519(mypriv,curve25519_basepoint9());
118
91f21771 119 ssecret = curve25519(mypriv,txidpub);
120 ssecret2 = curve25519(txidpriv,mypub);
d176d2df 121 if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
8138fad2 122 {
ae748eaf 123 vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
124 vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
dfe4c146 125 endiancpy((uint8_t *)&entropy,_entropy,32);
126 endiancpy((uint8_t *)&hentropy,_hentropy,32);
840a96ca 127 }
128 else
129 {
130 for (i=0; i<32; i++)
774b8b44 131 fprintf(stderr,"%02x",ssecret.bytes[i]);
840a96ca 132 fprintf(stderr," ssecret\n");
133 for (i=0; i<32; i++)
ac9816bb 134 fprintf(stderr,"%02x",ssecret2.bytes[i]);
840a96ca 135 fprintf(stderr," ssecret2 dont match\n");
136 }
8138fad2 137 return(hentropy);
138}
139
9025093e 140uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
10078e85 141{
b52f7685 142 uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
98c6e2e1 143 if ( odds < 10000 )
10078e85 144 return(0);
98c6e2e1 145 else odds -= 10000;
be7a410a 146 if ( bet < minbet || bet > maxbet || odds > maxodds )
147 {
148 fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
149 return(0);
150 }
151 endiancpy(buf,(uint8_t *)&houseentropy,32);
152 endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
153 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
208f0e7a 154 endiancpy((uint8_t *)&house,_house,32);
be7a410a 155
156 endiancpy(buf,(uint8_t *)&bettorentropy,32);
157 endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
158 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
208f0e7a 159 endiancpy((uint8_t *)&bettor,_bettor,32);
b52f7685 160 if ( odds > 1 )
161 bettor = (bettor / arith_uint256(odds));
162 if ( bettor >= house )
163 winnings = bet * odds;
164 else winnings = 0;
8a2eeb6b 165 fprintf(stderr,"bet %.8f at odds %d:1 %s vs %s\n",(double)bet/COIN,(int32_t)odds,uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
d0d96ab0 166 return(0);
10078e85 167}
168
9025093e 169CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
10078e85 170{
171 CScript opret; uint8_t evalcode = EVAL_DICE;
9025093e 172 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
10078e85 173 return(opret);
174}
175
9025093e 176uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
10078e85 177{
178 std::vector<uint8_t> vopret; uint8_t *script,e,f;
179 GetOpReturnData(scriptPubKey, vopret);
180 script = (uint8_t *)vopret.data();
9025093e 181 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 182 {
183 if ( e == EVAL_DICE && f == 'F' )
184 return(f);
185 }
186 return(0);
187}
188
afef48c0 189CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
10078e85 190{
191 CScript opret; uint8_t evalcode = EVAL_DICE;
afef48c0 192 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
10078e85 193 return(opret);
194}
195
54519f69 196uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
10078e85 197{
9025093e 198 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
753a6f92 199 GetOpReturnData(scriptPubKey,vopret);
10078e85 200 if ( vopret.size() > 2 )
201 {
202 script = (uint8_t *)vopret.data();
203 if ( script[0] == EVAL_DICE )
204 {
205 if ( script[1] == 'F' )
206 {
9025093e 207 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 208 {
72362bbe 209 memset(&hash,0,32);
10078e85 210 fundingtxid = txid;
211 return('F');
212 } else fprintf(stderr,"unmarshal error for F\n");
213 }
54519f69 214 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
10078e85 215 {
02409974 216 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
10078e85 217 return(f);
54519f69 218 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
10078e85 219 }
220 } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
221 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
222 return(0);
223}
cfea7a46 224
a03bb7fa 225uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
226{
54519f69 227 uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
228 if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
a03bb7fa 229 return(hash);
230 else return(zeroid);
231}
232
cfea7a46 233uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
234{
235 char destaddr[64];
236 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
237 {
238 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
239 return(tx.vout[v].nValue);
240 }
241 return(0);
242}
243
10078e85 244bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
cfea7a46 245{
246 static uint256 zerohash;
10078e85 247 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
cfea7a46 248 numvins = tx.vin.size();
249 numvouts = tx.vout.size();
250 for (i=0; i<numvins; i++)
251 {
cfea7a46 252 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
253 {
cfea7a46 254 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
10078e85 255 return eval->Invalid("always should find vin, but didnt");
cfea7a46 256 else
257 {
cfea7a46 258 if ( hashBlock == zerohash )
259 return eval->Invalid("cant dice from mempool");
260 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
261 inputs += assetoshis;
262 }
263 }
264 }
265 for (i=0; i<numvouts; i++)
266 {
267 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
268 if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
269 outputs += assetoshis;
270 }
10078e85 271 if ( inputs != outputs+txfee )
cfea7a46 272 {
273 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
10078e85 274 return eval->Invalid("mismatched inputs != outputs + txfee");
cfea7a46 275 }
276 else return(true);
277}
278
1c87477b 279bool DiceIsmine(const CScript scriptPubKey)
280{
281 char destaddr[64],myaddr[64];
282 Getscriptaddress(destaddr,scriptPubKey);
ca05cbaa 283 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
1c87477b 284 return(strcmp(destaddr,myaddr) == 0);
285}
286
aec296a5 287int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction vinTx,uint256 bettorentropy,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 fundingtxid)
753a6f92 288{
aec296a5 289 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
753a6f92 290 //char str[65],str2[65];
aec296a5 291 if ( DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout.size() > 0 )
753a6f92 292 {
54519f69 293 if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') && sbits == vinsbits && fundingtxid == vinfundingtxid )
753a6f92 294 {
295 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
296 if ( hentropy == hentropy2 )
297 {
298 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
299 //fprintf(stderr,"I am house entropy %.8f entropy.(%s) vs %s -> winnings %.8f\n",(double)vinTx.vout[0].nValue/COIN,uint256_str(str,entropy),uint256_str(str2,hash),(double)winnings/COIN);
300 if ( winnings == 0 )
301 {
302 // queue 'L' losing tx
303 return(-1);
304 }
305 else
306 {
307 // queue 'W' winning tx
308 return(1);
309 }
310 }
311 }
312 }
313 return(0);
314}
315
cfea7a46 316bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
317{
aec296a5 318 uint256 txid,fundingtxid,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
cfea7a46 319 numvins = tx.vin.size();
320 numvouts = tx.vout.size();
321 preventCCvins = preventCCvouts = -1;
322 if ( numvouts < 1 )
323 return eval->Invalid("no vouts");
324 else
325 {
10078e85 326 txid = tx.GetHash();
54519f69 327 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 328 {
10078e85 329 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
330 return eval->Invalid("cant find fundingtxid");
9025093e 331 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
10078e85 332 return eval->Invalid("fundingTx not valid");
333 switch ( funcid )
cfea7a46 334 {
10078e85 335 case 'F':
336 //vins.*: normal inputs
337 //vout.0: CC vout for funding
338 //vout.1: normal marker vout for easy searching
339 //vout.2: normal change
7eb60411 340 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
10078e85 341 return eval->Invalid("unexpected DiceValidate for createfunding");
342 break;
28b947b8 343 case 'E':
10078e85 344 //vins.*: normal inputs
0d4cfd0b 345 //vout.0: CC vout for locked entropy funds
346 //vout.1: tag to owner address for entropy funds
347 //vout.2: normal change
28b947b8 348 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
349 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
10078e85 350 break;
04cf9f60 351 case 'B':
28b947b8 352 //vin.0: entropy txid from house
353 //vins.1+: normal inputs
0d4cfd0b 354 //vout.0: CC vout for locked entropy
28b947b8 355 //vout.1: CC vout for locked bet
98c6e2e1 356 //vout.2: tag for bettor's address (txfee + odds)
28b947b8 357 //vout.3: change
358 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
1c87477b 359 if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
360 return eval->Invalid("always should find vin.0, but didnt");
aec296a5 361 if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1c87477b 362 {
753a6f92 363 fprintf(stderr,"DiceIsWinner.%d\n",iswin);
1c87477b 364 }
e0e985ee 365 //return eval->Invalid("dont confirm bet during debug");
10078e85 366 break;
5d3d3a87 367 case 'L':
7eb60411 368 //vin.0: betTx CC vout.0 entropy from bet
369 //vin.1: betTx CC vout.1 bet amount from bet
02409974 370 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 371 //vout.0: funding CC to entropy owner
372 //vout.1: tag to owner address for entropy funds
373 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy
5d3d3a87 374 break;
375 case 'W':
7eb60411 376 //vin.0: betTx CC vout.0 entropy from bet
377 //vin.1: betTx CC vout.1 bet amount from bet
02409974 378 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 379 //vout.0: funding CC change to entropy owner
380 //vout.1: tag to owner address for entropy funds
381 //vout.2: normal output to bettor's address
382 //vout.n-1: opreturn 'W' sbits fundingtxid hentropy
5d3d3a87 383 break;
02409974 384 case 'T':
7eb60411 385 //vin.0: betTx CC vout.0 entropy from bet
386 //vin.1: betTx CC vout.1 bet amount from bet
02409974 387 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 388 //vout.0: funding CC vin.0 to entropy owner
389 //vout.1: tag to owner address for entropy funds
390 //vout.2: normal vin.1 to bettor's address
02409974 391 //vout.n-1: opreturn 'T' sbits fundingtxid hentropy
7eb60411 392 /*for (i=0; i<numvins; i++)
10078e85 393 {
394 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
395 return eval->Invalid("unexpected normal vin for unlock");
396 }
397 if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
398 return false;
399 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
400 return eval->Invalid("always should find vin.0, but didnt");
401 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
402 return eval->Invalid("lock tx vout.0 is normal output");
403 else if ( tx.vout.size() < 3 )
404 return eval->Invalid("unlock tx not enough vouts");
405 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
406 return eval->Invalid("unlock tx vout.0 is normal output");
407 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
408 return eval->Invalid("unlock tx vout.1 is CC output");
409 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
410 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
411 amount = vinTx.vout[0].nValue;
98c6e2e1 412 reward = 0;
9025093e 413 //reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,timeoutblocks);
10078e85 414 if ( tx.vout[1].nValue > amount+reward )
415 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
7eb60411 416 preventCCvouts = 1;*/
10078e85 417 break;
cfea7a46 418 }
419 }
10078e85 420 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
cfea7a46 421 }
10078e85 422 return(true);
cfea7a46 423}
cfea7a46 424
10078e85 425uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
cfea7a46 426{
54519f69 427 char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid;
cfea7a46 428 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
429 GetCCaddress(cp,coinaddr,pk);
430 SetCCunspents(unspentOutputs,coinaddr);
431 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
432 {
433 txid = it->first.txhash;
10078e85 434 vout = (int32_t)it->first.index;
93ac96fa 435 if ( it->second.satoshis < 1000000 )
c2342f7b 436 continue;
10078e85 437 fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
438 for (j=0; j<mtx.vin.size(); j++)
439 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
440 break;
441 if ( j != mtx.vin.size() )
442 continue;
443 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
cfea7a46 444 {
54519f69 445 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 446 {
10078e85 447 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
02409974 448 if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'W' && funcid != 'L' && funcid != 'T' )
10078e85 449 continue;
7eb60411 450 else if ( fundsflag == 0 && funcid != 'B' )
10078e85 451 continue;
cfea7a46 452 if ( total != 0 && maxinputs != 0 )
10078e85 453 {
454 if ( fundsflag == 0 )
455 scriptPubKey = tx.vout[1].scriptPubKey;
456 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
457 }
458 totalinputs += it->second.satoshis;
cfea7a46 459 n++;
460 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
461 break;
10078e85 462 } else fprintf(stderr,"null funcid\n");
cfea7a46 463 }
464 }
465 return(totalinputs);
466}
467
04cf9f60 468uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
10078e85 469{
54519f69 470 char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CTransaction tx; int32_t vout,first=0; uint8_t funcid;
10078e85 471 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
472 GetCCaddress(cp,coinaddr,pk);
473 SetCCunspents(unspentOutputs,coinaddr);
04cf9f60 474 entropyval = 0;
10078e85 475 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
476 {
477 txid = it->first.txhash;
478 vout = (int32_t)it->first.index;
479 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
480 {
dd042a0f 481 char str[65],str2[65];
54519f69 482 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
10078e85 483 {
484 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
485 {
7eb60411 486 if ( funcid != 'B' && refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 )
04cf9f60 487 {
10078e85 488 totalinputs += nValue;
35d43add 489 fprintf(stderr,"add %.8f\n",(double)nValue/COIN);
02409974 490 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
04cf9f60 491 {
492 entropytxid = txid;
493 entropyval = tx.vout[0].nValue;
494 first = 1;
495 }
496 }
32e3be87 497 else fprintf(stderr,"%c refsbits.%llx sbits.%llx nValue %.8f\n",funcid,(long long)refsbits,(long long)sbits,(double)nValue/COIN);
76152f33 498 } else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
06811319 499 } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
10078e85 500 }
501 }
502 return(totalinputs);
503}
504
aec296a5 505bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
10078e85 506{
507 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
508 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
509 GetCCaddress(cp,CCaddr,dicepk);
3c3451b8 510 SetCCtxids(txids,cp->normaladdr);
aec296a5 511 if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
512 {
513 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
514 {
515 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
516 {
517 fundingPubKey = tx.vout[1].scriptPubKey;
518 return(true);
519 }
520 }
521 return(false);
522 }
10078e85 523 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
524 {
525 //int height = it->first.blockHeight;
526 txid = it->first.txhash;
a03bb7fa 527 if ( fundingtxid != zeroid && txid != fundingtxid )
528 continue;
10078e85 529 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
530 {
9025093e 531 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
10078e85 532 {
533 if ( sbits == refsbits )
a03bb7fa 534 {
aec296a5 535 fundingPubKey = tx.vout[1].scriptPubKey;
a03bb7fa 536 fundingtxid = txid;
10078e85 537 return(true);
a03bb7fa 538 }
10078e85 539 }
540 }
541 }
542 return(false);
543}
544
aec296a5 545struct CCcontract_info *Diceinit(CScript fundingPubKey,uint256 reffundingtxid,struct CCcontract_info *C,char *planstr,uint64_t &txfee,CPubKey &mypk,CPubKey &dicepk,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
a03bb7fa 546{
28500c30 547 struct CCcontract_info *cp; int32_t cmpflag;
a03bb7fa 548 cp = CCinit(C,EVAL_DICE);
549 if ( txfee == 0 )
550 txfee = 10000;
551 mypk = pubkey2pk(Mypubkey());
552 dicepk = GetUnspendable(cp,0);
553 sbits = stringbits(planstr);
554 if ( reffundingtxid == zeroid )
555 cmpflag = 0;
556 else cmpflag = 1;
28500c30 557 if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
a03bb7fa 558 {
97b0bc3a 559 fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
a03bb7fa 560 return(0);
561 }
562 return(cp);
563}
564
10078e85 565UniValue DiceInfo(uint256 diceid)
566{
f516779c 567 UniValue result(UniValue::VOBJ); CPubKey dicepk; uint256 hashBlock,entropytxid; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,funding,entropyval; char str[67],numstr[65]; struct CCcontract_info *cp,C;
10078e85 568 if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
569 {
570 fprintf(stderr,"cant find fundingtxid\n");
571 result.push_back(Pair("error","cant find fundingtxid"));
572 return(result);
573 }
9025093e 574 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
10078e85 575 {
576 fprintf(stderr,"fundingtxid isnt dice creation txid\n");
577 result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
578 return(result);
579 }
580 result.push_back(Pair("result","success"));
581 result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
582 unstringbits(str,sbits);
583 result.push_back(Pair("name",str));
584 result.push_back(Pair("sbits",sbits));
d0d96ab0 585 sprintf(numstr,"%.8f",(double)minbet/COIN);
586 result.push_back(Pair("minbet",numstr));
587 sprintf(numstr,"%.8f",(double)maxbet/COIN);
588 result.push_back(Pair("maxbet",numstr));
589 result.push_back(Pair("maxodds",maxodds));
9025093e 590 result.push_back(Pair("timeoutblocks",timeoutblocks));
f516779c 591 cp = CCinit(&C,EVAL_DICE);
592 dicepk = GetUnspendable(cp,0);
688d3ac4 593 funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
f516779c 594 sprintf(numstr,"%.8f",(double)funding/COIN);
10078e85 595 result.push_back(Pair("funding",numstr));
596 return(result);
597}
598
599UniValue DiceList()
cfea7a46 600{
9025093e 601 UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65];
10078e85 602 cp = CCinit(&C,EVAL_DICE);
603 SetCCtxids(addressIndex,cp->normaladdr);
604 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
605 {
606 txid = it->first.txhash;
607 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
608 {
9025093e 609 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
10078e85 610 {
611 result.push_back(uint256_str(str,txid));
612 }
613 }
614 }
615 return(result);
616}
617
9025093e 618std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
05177cfb 619{
aec296a5 620 CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
9025093e 621 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
05177cfb 622 {
623 fprintf(stderr,"negative parameter error\n");
624 return(0);
625 }
aec296a5 626 memset(&zero,0,sizeof(zero));
627 if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
05177cfb 628 return(0);
32e3be87 629 if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
10078e85 630 {
631 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
32e3be87 632 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
10078e85 633 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
9025093e 634 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
10078e85 635 }
636 fprintf(stderr,"cant find enough inputs\n");
637 return(0);
638}
639
640std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
641{
aec296a5 642 CMutableTransaction mtx; CScript fundingPubKey,scriptPubKey; uint256 entropy,hentropy; CPubKey mypk,dicepk; uint64_t sbits; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds,timeoutblocks;
10078e85 643 if ( amount < 0 )
644 {
645 fprintf(stderr,"negative parameter error\n");
646 return(0);
647 }
aec296a5 648 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
10078e85 649 return(0);
aec296a5 650 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
792cef06 651 {
652 uint8_t *ptr0,*ptr1; int32_t i;
653 ptr0 = (uint8_t *)scriptPubKey.data();
654 ptr1 = (uint8_t *)fundingPubKey.data();
655 for (i=0; i<35; i++)
656 fprintf(stderr,"%02x",ptr0[i]);
657 fprintf(stderr," script vs ");
658 for (i=0; i<35; i++)
659 fprintf(stderr,"%02x",ptr1[i]);
660 fprintf(stderr," funding\n");
661 }
aec296a5 662 if ( scriptPubKey == fundingPubKey )
10078e85 663 {
aec296a5 664 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 )
665 {
666 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
667 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
668 mtx.vout.push_back(CTxOut(txfee,scriptPubKey));
669 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
670 } else fprintf(stderr,"cant find enough inputs\n");
671 } else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
cfea7a46 672 return(0);
673}
674
7347a801 675std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
5d3d3a87 676{
aec296a5 677 CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropy,hentropy; struct CCcontract_info *cp,C;
7347a801 678 if ( bet < 0 || odds < 1 )
5d3d3a87 679 {
680 fprintf(stderr,"negative parameter error\n");
681 return(0);
682 }
aec296a5 683 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
7347a801 684 return(0);
685 if ( bet < minbet || bet > maxbet || odds > maxodds )
686 {
687 fprintf(stderr,"Dice plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds);
688 return(0);
689 }
7eb60411 690 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
7347a801 691 {
692 mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
693 if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
694 {
695 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
696 mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
697 mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
698 mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
afef48c0 699 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
7347a801 700 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
701 }
702 if ( entropyval == 0 && funding != 0 )
703 fprintf(stderr,"cant find dice entropy inputs\n");
704 else fprintf(stderr,"cant find dice inputs\n");
705 return(0);
706}
707
02409974 708std::string DiceWinLoseTimeout(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
7347a801 709{
121e371d 710 CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CPubKey mypk,dicepk; struct CCcontract_info *cp,C; int64_t inputs,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid; int32_t iswin; uint64_t entropyval,sbits;
aec296a5 711 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
5d3d3a87 712 return(0);
aec296a5 713 if ( winlosetimeout != 0 )
714 {
715 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
716 if ( scriptPubKey != fundingPubKey )
717 {
718 fprintf(stderr,"only dice fund creator can submit winner or loser\n");
719 return(0);
720 }
721 }
a03bb7fa 722 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
5d3d3a87 723 {
a03bb7fa 724 bettorentropy = DiceGetEntropy(betTx,'B');
aec296a5 725 if ( (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
a03bb7fa 726 {
02409974 727 if ( iswin == winlosetimeout )
a03bb7fa 728 {
7eb60411 729 fprintf(stderr,"iswin.%d matches\n",iswin);
730 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
731 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
a03bb7fa 732 if ( iswin == 0 )
7eb60411 733 {
02409974 734 funcid = 'T';
735 fprintf(stderr,"timeout refunds are not supported yet\n");
7eb60411 736 }
a03bb7fa 737 else if ( iswin > 0 )
7eb60411 738 {
a03bb7fa 739 funcid = 'W';
7eb60411 740 odds = (betTx.vout[2].nValue - txfee);
741 if ( odds < 1 || odds > maxodds )
742 {
743 fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
744 return(0);
745 }
746 CCchange = betTx.vout[0].nValue;
747 fundsneeded = 2*txfee + (odds-1)*betTx.vout[1].nValue;
748 if ( (inputs= AddDiceInputs(scriptPubKey,1,cp,mtx,dicepk,fundsneeded,60)) > 0 )
749 {
750 if ( inputs > fundsneeded+txfee )
751 CCchange += (inputs - (fundsneeded+txfee));
752 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
753 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
754 mtx.vout.push_back(CTxOut(odds * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
755 }
756 else
757 {
758 fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
759 return(0);
760 }
761 }
762 else
a03bb7fa 763 {
7eb60411 764 funcid = 'L';
765 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue - txfee,dicepk));
766 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
767 }
768 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
afef48c0 769 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
e0ea3b3e 770 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
a03bb7fa 771 } else return(0);
772 }
5d3d3a87 773 return(0);
774}
775
This page took 0.19739 seconds and 4 git commands to generate.