1 /******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
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. *
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 *
12 * Removal or modification of this copyright notice is prohibited. *
14 ******************************************************************************/
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.
23 1. CC vout locks "house" funds with hash(entropy)
24 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout.
25 3. house account sends funds to winner/loser with proof of entropy
26 4. if timeout, bettor gets refund
28 2. and 3. can be done in mempool
30 The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction.
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.
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.
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
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.
42 vout.0: CC vout for funding
44 vout.2: dice marker address vout for easy searching
46 vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
50 vout.0: CC vout for locked entropy funds
51 vout.1: tag to owner address for entropy funds
53 vout.n-1: opreturn 'E' sbits fundingtxid hentropy
56 vin.0: entropy txid from house (must validate vin0 of 'E')
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)
62 vout.n-1: opreturn 'B' sbits fundingtxid entropy
66 vin.1: betTx CC vout.0 entropy from bet
67 vin.2: betTx CC vout.1 bet amount from bet
68 vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
69 vout.0: funding CC to entropy owner
70 vout.1: tag to owner address for entropy funds
71 vout.2: change to fundingpk
72 vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
75 same as loser, but vout.2 is winnings
76 vout.3: change to fundingpk
77 vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
80 same as winner, just without hentropy or proof
82 WARNING: there is an attack vector that precludes betting any large amounts, it goes as follows:
83 1. do dicebet to get the house entropy revealed
84 2. calculate bettor entropy that would win against the house entropy
85 3. reorg the chain and make a big bet using the winning entropy calculated in 2.
87 In order to mitigate this, the disclosure of the house entropy needs to be delayed beyond a reasonable reorg depth (notarization). It is recommended for production dice game with significant amounts of money to use such a delayed disclosure method.
90 #include "../compat/endian.h"
92 static uint256 bettxids[128];
94 struct dicefinish_info
96 uint256 fundingtxid,bettxid;
101 bool mySendrawtransaction(std::string res)
103 CTransaction tx; char str[65];
104 if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
106 if ( DecodeHexTx(tx,res) != 0 )
108 fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash()));
110 if ( myAddtomempool(tx) != 0 )
112 RelayTransaction(tx);
113 fprintf(stderr,"added to mempool and broadcast\n");
115 } else fprintf(stderr,"error adding to mempool\n");
116 } else fprintf(stderr,"error decoding hex\n");
121 void *dicefinish(void *_ptr)
123 char str[65],str2[65],name[32]; std::string res; int32_t i,result,duplicate=0; struct dicefinish_info *ptr;
124 ptr = (struct dicefinish_info *)_ptr;
125 sleep(3); // wait for bettxid to be in mempool
126 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
127 if ( bettxids[i] == ptr->bettxid )
132 if ( duplicate == 0 )
134 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
135 if ( bettxids[i] == zeroid )
137 bettxids[i] = ptr->bettxid;
140 if ( i == sizeof(bettxids)/sizeof(*bettxids) )
141 bettxids[rand() % i] = ptr->bettxid;
143 unstringbits(name,ptr->sbits);
144 //fprintf(stderr,"duplicate.%d dicefinish.%d %s funding.%s bet.%s\n",duplicate,ptr->iswin,name,uint256_str(str,ptr->fundingtxid),uint256_str(str2,ptr->bettxid));
145 if ( duplicate == 0 )
147 res = DiceBetFinish(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
149 mySendrawtransaction(res);
155 void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
157 struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
158 ptr->fundingtxid = fundingtxid;
159 ptr->bettxid = bettxid;
162 if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
164 //fprintf(stderr,"DiceQueue.%d\n",iswin);
165 } // small memory leak per DiceQueue
168 CPubKey DiceFundingPk(CScript scriptPubKey)
170 CPubKey pk; uint8_t *ptr,*dest; int32_t i;
171 if ( scriptPubKey.size() == 35 )
173 dest = (uint8_t *)pk.begin();
175 dest[i] = scriptPubKey[i+1];
176 } else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size());
180 uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
182 int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
183 memset(&hentropy,0,32);
184 endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
185 txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
186 txidpub = curve25519(txidpriv,curve25519_basepoint9());
188 Myprivkey(tmp256.bytes);
189 vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
190 mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
191 mypub = curve25519(mypriv,curve25519_basepoint9());
193 ssecret = curve25519(mypriv,txidpub);
194 ssecret2 = curve25519(txidpriv,mypub);
195 if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
197 vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
198 vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
199 endiancpy((uint8_t *)&entropy,_entropy,32);
200 endiancpy((uint8_t *)&hentropy,_hentropy,32);
205 fprintf(stderr,"%02x",ssecret.bytes[i]);
206 fprintf(stderr," ssecret\n");
208 fprintf(stderr,"%02x",ssecret2.bytes[i]);
209 fprintf(stderr," ssecret2 dont match\n");
211 //char str[65],str2[65];
212 //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy));
216 int32_t dice_5nibbles(uint8_t *fivevals)
218 return(((int32_t)fivevals[0]<<16) + ((int32_t)fivevals[1]<<12) + ((int32_t)fivevals[2]<<8) + ((int32_t)fivevals[3]<<4) + ((int32_t)fivevals[4]));
221 uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
223 uint8_t buf[64],_house[32],_bettor[32],_hash[32],hash[32],hash16[64]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65]; int32_t i,modval;
227 if ( bet < minbet || bet > maxbet )
229 CCerror = strprintf("bet size violation %.8f",(double)bet/COIN);
230 fprintf(stderr,"%s\n", CCerror.c_str() );
233 if ( odds > maxodds )
235 CCerror = strprintf("invalid odds %d, must be <= %d",odds, maxodds);
236 fprintf(stderr,"%s\n", CCerror.c_str() );
239 //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,houseentropy),uint256_str(str2,bettorentropy));
241 endiancpy(buf,(uint8_t *)&houseentropy,32);
242 endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
243 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
244 endiancpy((uint8_t *)&house,_house,32);
246 endiancpy(buf,(uint8_t *)&bettorentropy,32);
247 endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
248 vcalc_sha256(0,(uint8_t *)&_bettor,buf,64);
249 endiancpy((uint8_t *)&bettor,_bettor,32);
255 bettor = (bettor / arith_uint256(odds));
256 if ( bettor >= house )
257 winnings = bet * (odds+1);
260 if ( odds > 9999 ) // shouldnt happen
262 endiancpy(buf,(uint8_t *)&house,32);
263 endiancpy(&buf[32],(uint8_t *)&bettor,32);
264 vcalc_sha256(0,(uint8_t *)&_hash,buf,64);
265 endiancpy(hash,_hash,32);
268 hash16[i<<1] = ((hash[i] >> 4) & 0x0f);
269 hash16[(i<<1) + 1] = (hash[i] & 0x0f);
274 modval = dice_5nibbles(&hash16[i*5]);
275 if ( modval < 1000000 )
281 fprintf(stderr,"modval %d vs %d\n",modval,(int32_t)(10000/(odds+1)));
282 if ( modval < 10000/(odds+1) )
283 winnings = bet * (odds+1);
285 else if ( bettor >= house )
286 winnings = bet * (odds+1);
290 CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
292 CScript opret; uint8_t evalcode = EVAL_DICE;
293 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
297 uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
299 std::vector<uint8_t> vopret; uint8_t *script,e,f;
300 GetOpReturnData(scriptPubKey, vopret);
301 script = (uint8_t *)vopret.data();
302 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
304 if ( e == EVAL_DICE && f == 'F' )
310 CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
312 CScript opret; uint8_t evalcode = EVAL_DICE;
313 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
317 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
319 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
320 GetOpReturnData(scriptPubKey,vopret);
321 if ( vopret.size() > 2 )
323 script = (uint8_t *)vopret.data();
324 if ( script[0] == EVAL_DICE )
326 if ( script[1] == 'F' )
328 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
333 } else fprintf(stderr,"unmarshal error for F\n");
335 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
337 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
339 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
341 } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
342 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
346 uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
348 uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
349 if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
354 uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
356 char destaddr[64]; uint8_t funcid; int32_t numvouts; uint64_t sbits; uint256 fundingtxid,hash,proof;
357 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
359 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 && (numvouts= tx.vout.size()) > 0 )
361 if ( (funcid= DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits && ((funcid == 'F' && tx.GetHash() == reffundingtxid) || fundingtxid == reffundingtxid) )
362 return(tx.vout[v].nValue);
368 int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t refsbits,uint256 reffundingtxid)
370 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis;
371 numvins = tx.vin.size();
372 numvouts = tx.vout.size();
373 inputs = outputs = 0;
374 for (i=0; i<numvins; i++)
376 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
378 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
379 return eval->Invalid("always should find vin, but didnt");
382 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
383 inputs += assetoshis;
387 for (i=0; i<numvouts; i++)
389 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
390 if ( (assetoshis= IsDicevout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
391 outputs += assetoshis;
393 return(inputs - outputs);
396 bool DiceIsmine(const CScript scriptPubKey)
398 char destaddr[64],myaddr[64];
399 Getscriptaddress(destaddr,scriptPubKey);
400 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
401 return(strcmp(destaddr,myaddr) == 0);
404 int32_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)
406 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
407 //char str[65],str2[65];
408 if ( vinTx.vout.size() > 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
410 if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L') && sbits == vinsbits && fundingtxid == vinfundingtxid )
412 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
413 if ( hentropy == hentropy2 )
415 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
416 char str[65]; fprintf(stderr,"%s winnings %.8f bet %.8f at odds %d:1\n",uint256_str(str,tx.GetHash()),(double)winnings/COIN,(double)tx.vout[1].nValue/COIN,(int32_t)(tx.vout[2].nValue-10000));
417 //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);
420 // queue 'L' losing tx
425 // queue 'W' winning tx
428 } else fprintf(stderr,"hentropy != hentropy2\n");
429 } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid);
430 } //else fprintf(stderr,"notmine or not CC\n");
434 bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
437 if ( CCduration(numblocks,betTx.GetHash()) <= 0 )
439 return(numblocks >= timeoutblocks);
442 bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn)
444 uint256 txid,fundingtxid,vinfundingtxid,vinhentropy,vinproof,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks,odds,winnings; uint64_t vinsbits,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64];
445 numvins = tx.vin.size();
446 numvouts = tx.vout.size();
447 preventCCvins = preventCCvouts = -1;
449 return eval->Invalid("no vouts");
453 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
455 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
456 return eval->Invalid("cant find fundingtxid");
457 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
458 return eval->Invalid("fundingTx not valid");
459 if ( maxodds > 9999 )
460 return eval->Invalid("maxodds too big");
461 fundingPubKey = fundingTx.vout[1].scriptPubKey;
465 //vins.*: normal inputs
466 //vout.0: CC vout for funding
467 //vout.1: normal marker vout for easy searching
468 //vout.2: normal change
469 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
470 return eval->Invalid("unexpected DiceValidate for createfunding");
472 case 'E': // check sig of vin to match fundingtxid in the 'B' tx
473 //vins.*: normal inputs
474 //vout.0: CC vout for locked entropy funds
475 //vout.1: tag to owner address for entropy funds
476 //vout.2: normal change
477 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
478 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
481 //vin.0: entropy txid from house
482 //vins.1+: normal inputs
483 //vout.0: CC vout for locked entropy
484 //vout.1: CC vout for locked bet
485 //vout.2: tag for bettor's address (txfee + odds)
487 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
490 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
491 return eval->Invalid("vin.0 is normal for bet");
492 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
493 return eval->Invalid("vout.0 is normal for bet");
494 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
495 return eval->Invalid("vout.1 is normal for bet");
496 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
497 return eval->Invalid("always should find vin.0, but didnt for bet");
498 else if ( vinTx.vout[1].scriptPubKey != fundingPubKey )
499 return eval->Invalid("entropy tx not fundingPubKey for bet");
500 else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 )
501 return eval->Invalid("vout[0] != entropy nValue for bet");
502 else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 )
503 return eval->Invalid("vout[1] constrain violation for bet");
504 else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue < txfee )
505 return eval->Invalid("vout[2] nValue violation for bet");
506 else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 )
507 return eval->Invalid("always should find vinofvin.0, but didnt for bet");
508 else if ( vinTx.vin[0].prevout.hash != fundingtxid )
510 if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
512 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
513 fprintf(stderr,"bidTx.%s\n",uint256_str(str,txid));
514 fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n);
515 fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n);
516 const CScript &s0 = vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey;
517 for (i=0; i<vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.size(); i++)
518 fprintf(stderr,"%02x",s0[i]);
519 fprintf(stderr," script vs ");
520 for (i=0; i<fundingPubKey.size(); i++)
521 fprintf(stderr,"%02x",fundingPubKey[i]);
522 fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,vinTx.vin[0].prevout.n,uint256_str(str,vinTx.vin[0].prevout.hash));
523 return eval->Invalid("vin1 of entropy tx not fundingPubKey for bet");
526 if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
528 // will only happen for fundingPubKey
529 DiceQueue(iswin,sbits,fundingtxid,txid);
532 // make sure all funding txid are from matching sbits and fundingtxid!!
536 //vin.0: normal input
537 //vin.1: betTx CC vout.0 entropy from bet
538 //vin.2: betTx CC vout.1 bet amount from bet
539 //vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
540 //vout.1: tag to owner address for entropy funds
542 DiceAmounts(inputs,outputs,cp,eval,tx,sbits,fundingtxid);
543 if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 )
544 return eval->Invalid("vin0 or vin1 normal vin for bet");
545 else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash )
546 return eval->Invalid("vin0 != vin1 prevout.hash for bet");
547 else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
548 return eval->Invalid("always should find vin.0, but didnt for wlt");
549 else if ( vinTx.vout.size() < 3 || DecodeDiceOpRet(tx.vin[1].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,vinhentropy,vinproof) != 'B' )
550 return eval->Invalid("not betTx for vin0/1 for wlt");
551 else if ( sbits != vinsbits || fundingtxid != vinfundingtxid )
552 return eval->Invalid("sbits or fundingtxid mismatch for wlt");
553 else if ( fundingPubKey != tx.vout[1].scriptPubKey )
554 return eval->Invalid("tx.vout[1] != fundingPubKey for wlt");
557 //vout.0: funding CC to entropy owner
558 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
559 if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 )
560 return eval->Invalid("vout[0] != inputs-txfee for loss");
561 else if ( tx.vout[2].scriptPubKey != fundingPubKey )
563 if ( tx.vout[2].scriptPubKey.size() == 0 || tx.vout[2].scriptPubKey[0] != 0x6a )
564 return eval->Invalid("vout[2] not send to fundingPubKey for loss");
570 //vout.0: funding CC change to entropy owner
571 //vout.2: normal output to bettor's address
572 //vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
573 odds = vinTx.vout[2].nValue - txfee;
574 if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
575 return eval->Invalid("vout[0] != inputs-txfee for win/timeout");
576 else if ( tx.vout[2].scriptPubKey != vinTx.vout[2].scriptPubKey )
577 return eval->Invalid("vout[2] scriptPubKey mismatch for win/timeout");
578 else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue )
579 return eval->Invalid("vout[2] payut mismatch for win/timeout");
580 else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) )
582 fprintf(stderr,"inputs %.8f != outputs %.8f + %.8f\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[2].nValue/COIN);
583 return eval->Invalid("CC funds mismatch for win/timeout");
585 else if ( tx.vout[3].scriptPubKey != fundingPubKey )
587 if ( tx.vout[3].scriptPubKey.size() == 0 || tx.vout[3].scriptPubKey[0] != 0x6a )
588 return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout");
590 iswin = (funcid == 'W');
594 //char str[65],str2[65];
595 entropy = DiceGetEntropy(vinTx,'B');
596 vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32);
597 //fprintf(stderr,"calculated house hentropy.%s\n",uint256_str(str,hash));
598 //fprintf(stderr,"verify house entropy %s vs bettor %s\n",uint256_str(str,proof),uint256_str(str2,entropy));
599 winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy);
600 if ( (winnings == 0 && iswin > 0) || (winnings > 0 && iswin < 0) )
601 return eval->Invalid("DiceCalc mismatch for win/loss");
603 else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 )
604 return eval->Invalid("invalid timeout claim for timeout");
608 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
613 uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
615 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;
616 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
617 GetCCaddress(cp,coinaddr,pk);
618 SetCCunspents(unspentOutputs,coinaddr);
619 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
621 txid = it->first.txhash;
622 vout = (int32_t)it->first.index;
623 if ( it->second.satoshis < 1000000 )
625 //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
626 for (j=0; j<mtx.vin.size(); j++)
627 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
629 if ( j != mtx.vin.size() )
631 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
633 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
635 char str[65],sstr[16];
636 unstringbits(sstr,sbits);
637 fprintf(stderr,"(%c) %.8f %s %s\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid));
638 if ( sbits == refsbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
640 if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
642 if ( total != 0 && maxinputs != 0 )
643 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
644 totalinputs += it->second.satoshis;
646 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
650 } else fprintf(stderr,"null funcid\n");
656 int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid)
658 char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0; uint8_t funcid;
659 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
661 entropytxid = zeroid;
662 if ( GetTransaction(reffundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 )
664 fundingPubKey = tx.vout[1].scriptPubKey;
666 GetCCaddress(cp,coinaddr,dicepk);
667 SetCCunspents(unspentOutputs,coinaddr);
669 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
671 txid = it->first.txhash;
672 vout = (int32_t)it->first.index;
673 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
675 //char str[65],str2[65];
676 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
678 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
680 if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
682 fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN);
683 if ( funcid != 'F' && funcid != 'T' )
685 totalinputs += nValue;
686 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
688 fprintf(stderr,"check first\n");
689 if ( fundingPubKey == tx.vout[1].scriptPubKey )
691 if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash )
693 if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 )
695 fprintf(stderr,"cant find entropy vin0 %s or vin0prev %d vouts[%d]\n",uint256_str(str,tx.vin[0].prevout.hash),tx.vin[0].prevout.n,(int32_t)vinTx.vout.size());
698 if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
700 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
701 const CScript &s0 = vinTx.vout[tx.vin[0].prevout.n].scriptPubKey;
702 for (i=0; i<vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.size(); i++)
703 fprintf(stderr,"%02x",s0[i]);
704 fprintf(stderr," script vs ");
705 for (i=0; i<fundingPubKey.size(); i++)
706 fprintf(stderr,"%02x",fundingPubKey[i]);
707 fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
710 } //else fprintf(stderr,"not E or is funding\n");
712 entropyval = tx.vout[0].nValue;
717 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
718 const CScript &s0 = tx.vout[1].scriptPubKey;
719 for (i=0; i<tx.vout[1].scriptPubKey.size(); i++)
720 fprintf(stderr,"%02x",s0[i]);
721 fprintf(stderr," script vs ");
722 for (i=0; i<fundingPubKey.size(); i++)
723 fprintf(stderr,"%02x",fundingPubKey[i]);
724 fprintf(stderr," (%c) tx vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
727 } else fprintf(stderr,"%s %c refsbits.%llx sbits.%llx nValue %.8f\n",uint256_str(str,txid),funcid,(long long)refsbits,(long long)sbits,(double)nValue/COIN);
728 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
729 } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
732 fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN);
736 bool 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)
738 char CCaddr[64]; uint64_t sbits=0; uint256 txid,hashBlock; CTransaction tx;
739 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
740 GetCCaddress(cp,CCaddr,dicepk);
741 SetCCtxids(txids,cp->normaladdr);
742 if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
744 //fprintf(stderr,"check fundingtxid\n");
745 if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
747 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
749 fundingPubKey = tx.vout[1].scriptPubKey;
751 } else fprintf(stderr,"error decoding opret or sbits mismatch %llx vs %llx\n",(long long)sbits,(long long)refsbits);
752 } else fprintf(stderr,"couldnt get funding tx\n");
755 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
757 //int height = it->first.blockHeight;
758 txid = it->first.txhash;
759 if ( fundingtxid != zeroid && txid != fundingtxid )
761 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
763 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
765 if ( sbits == refsbits )
767 fundingPubKey = tx.vout[1].scriptPubKey;
777 struct 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)
779 struct CCcontract_info *cp; int32_t cmpflag;
780 cp = CCinit(C,EVAL_DICE);
783 mypk = pubkey2pk(Mypubkey());
784 dicepk = GetUnspendable(cp,0);
785 sbits = stringbits(planstr);
786 if ( reffundingtxid == zeroid )
789 if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
791 fprintf(stderr,"Dice plan (%s) exists.%d vs cmpflag.%d\n",planstr,!cmpflag,cmpflag);
797 UniValue DiceInfo(uint256 diceid)
799 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;
800 if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
802 fprintf(stderr,"cant find fundingtxid\n");
803 ERR_RESULT("cant find fundingtxid");
806 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
808 fprintf(stderr,"fundingtxid isnt dice creation txid\n");
809 ERR_RESULT("fundingtxid isnt dice creation txid");
812 result.push_back(Pair("result","success"));
813 result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
814 unstringbits(str,sbits);
815 result.push_back(Pair("name",str));
816 result.push_back(Pair("sbits",sbits));
817 sprintf(numstr,"%.8f",(double)minbet/COIN);
818 result.push_back(Pair("minbet",numstr));
819 sprintf(numstr,"%.8f",(double)maxbet/COIN);
820 result.push_back(Pair("maxbet",numstr));
821 result.push_back(Pair("maxodds",maxodds));
822 result.push_back(Pair("timeoutblocks",timeoutblocks));
823 cp = CCinit(&C,EVAL_DICE);
824 dicepk = GetUnspendable(cp,0);
825 funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
826 sprintf(numstr,"%.8f",(double)funding/COIN);
827 result.push_back(Pair("funding",numstr));
833 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];
834 cp = CCinit(&C,EVAL_DICE);
835 SetCCtxids(addressIndex,cp->normaladdr);
836 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
838 txid = it->first.txhash;
839 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
841 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
843 result.push_back(uint256_str(str,txid));
850 std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
852 CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
853 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || maxodds > 9999 || timeoutblocks < 0 || timeoutblocks > 1440 )
855 CCerror = "invalid parameter error";
856 fprintf(stderr,"%s\n", CCerror.c_str() );
859 if ( funds < 100*COIN )
861 CCerror = "dice plan needs at least 100 coins";
862 fprintf(stderr,"%s\n", CCerror.c_str() );
865 memset(&zero,0,sizeof(zero));
866 if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
868 CCerror = "Diceinit error in create funding";
869 fprintf(stderr,"%s\n", CCerror.c_str() );
872 if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 )
874 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
875 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
876 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
877 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
879 CCerror = "cant find enough inputs";
880 fprintf(stderr,"%s\n", CCerror.c_str() );
884 std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
886 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;
889 CCerror = "amount must be positive";
890 fprintf(stderr,"%s\n", CCerror.c_str() );
893 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
895 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
898 uint8_t *ptr0,*ptr1; int32_t i;
900 fprintf(stderr,"%02x",scriptPubKey[i]);
901 fprintf(stderr," script vs ");
903 fprintf(stderr,"%02x",fundingPubKey[i]);
904 fprintf(stderr," funding\n");
906 if ( scriptPubKey == fundingPubKey )
908 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 )
910 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
911 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
912 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
913 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
915 CCerror = "cant find enough inputs";
916 fprintf(stderr,"%s\n", CCerror.c_str() );
919 CCerror = "only fund creator can add more funds (entropy)";
920 fprintf(stderr,"%s\n", CCerror.c_str() );
925 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
927 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;
930 CCerror = "bet must be positive";
931 fprintf(stderr,"%s\n", CCerror.c_str() );
934 if ( odds < 1 || odds > 9999 )
936 CCerror = "odds must be between 1 and 9999";
937 fprintf(stderr,"%s\n", CCerror.c_str() );
940 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
942 if ( bet < minbet || bet > maxbet || odds > maxodds )
944 CCerror = strprintf("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);
945 fprintf(stderr,"%s\n", CCerror.c_str() );
948 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
950 if ( myIsutxo_spentinmempool(entropytxid,0) != 0 )
952 CCerror = "entropy txid is spent";
953 fprintf(stderr,"%s\n", CCerror.c_str() );
956 mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
957 if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
959 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
960 mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
961 mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
962 mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
963 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
964 } else fprintf(stderr,"cant find enough normal inputs for %.8f, plan funding %.8f\n",(double)bet/COIN,(double)funding/COIN);
966 if ( entropyval == 0 && funding != 0 )
967 CCerror = "cant find dice entropy inputs";
969 CCerror = "cant find dice input";
970 fprintf(stderr,"%s\n", CCerror.c_str() );
974 std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
976 CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid=0; int32_t iswin=0; uint64_t entropyval,sbits;
978 //char str[65]; fprintf(stderr,"DiceBetFinish.%s %s\n",planstr,uint256_str(str,bettxid));
979 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
981 CCerror = "Diceinit error in finish";
982 fprintf(stderr,"%s\n", CCerror.c_str() );
985 fundingpk = DiceFundingPk(fundingPubKey);
986 if ( winlosetimeout != 0 )
988 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
989 if ( scriptPubKey != fundingPubKey )
991 //fprintf(stderr,"only dice fund creator can submit winner or loser\n");
995 if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 )
997 CCerror = "no txfee inputs for win/lose";
998 fprintf(stderr,"%s\n", CCerror.c_str() );
1001 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
1003 bettorentropy = DiceGetEntropy(betTx,'B');
1004 if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1006 if ( winlosetimeout != 0 )
1007 winlosetimeout = iswin;
1008 if ( iswin == winlosetimeout )
1010 if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 )
1012 CCerror = "bettxid already spent";
1013 fprintf(stderr,"%s\n", CCerror.c_str() );
1016 //fprintf(stderr,"iswin.%d matches\n",iswin);
1017 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
1018 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
1022 if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
1028 hentropy = hentropyproof = zeroid;
1034 if ( funcid != 'T' )
1036 odds = (betTx.vout[2].nValue - txfee);
1037 if ( odds < 1 || odds > maxodds )
1039 CCerror = strprintf("illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
1040 fprintf(stderr,"%s\n", CCerror.c_str() );
1043 CCchange = betTx.vout[0].nValue + betTx.vout[1].nValue;
1044 fundsneeded = txfee + (odds+1)*betTx.vout[1].nValue;
1045 if ( CCchange >= fundsneeded )
1046 CCchange -= fundsneeded;
1047 else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 )
1049 if ( inputs > fundsneeded )
1050 CCchange += (inputs - fundsneeded);
1054 CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN);
1055 fprintf(stderr,"%s\n", CCerror.c_str() );
1058 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
1059 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
1060 mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
1065 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk));
1066 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
1068 if ( winlosetimeout != 0 )
1069 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
1071 //char str[65],str2[65];
1072 //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy));
1073 return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
1074 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
1079 fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
1084 CCerror = "couldnt find bettx or entropytx";
1085 fprintf(stderr,"%s\n", CCerror.c_str() );
1089 double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid)
1091 CScript fundingPubKey,scriptPubKey; CTransaction spenttx,betTx; uint256 hash,proof,txid,hashBlock,spenttxid; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int32_t i,result,vout,n=0; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits; char coinaddr[64]; std::string res;
1092 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1094 CCerror = "Diceinit error in status";
1095 fprintf(stderr,"%s\n", CCerror.c_str() );
1098 fundingpk = DiceFundingPk(fundingPubKey);
1099 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
1100 GetCCaddress(cp,coinaddr,dicepk);
1101 if ( bettxid == zeroid ) // scan
1103 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1104 SetCCunspents(unspentOutputs,coinaddr);
1105 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
1107 txid = it->first.txhash;
1108 vout = (int32_t)it->first.index;
1109 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
1111 if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' )
1113 res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey);
1116 mySendrawtransaction(res);
1122 if ( 0 && scriptPubKey == fundingPubKey )
1124 for (i=0; i<=n; i++)
1126 res = DiceAddfunding(txfee,planstr,fundingtxid,COIN);
1127 fprintf(stderr,"ENTROPY tx:\n");
1128 mySendrawtransaction(res);
1136 if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1138 //fprintf(stderr,"bettx is spent\n");
1139 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() > 2 )
1141 //fprintf(stderr,"found spenttxid %s\n",uint256_str(str,spenttxid));
1142 if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
1144 else return((double)spenttx.vout[2].nValue/COIN);
1146 CCerror = "couldnt find bettx or spenttx %s\n",uint256_str(str,spenttxid);
1147 fprintf(stderr,"%s\n", CCerror.c_str());
1150 else if ( scriptPubKey == fundingPubKey )
1151 res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,1);
1152 else res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,0);
1155 mySendrawtransaction(res);
1157 if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1159 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() >= 2 )
1161 if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
1162 //if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a )
1164 else return((double)spenttx.vout[2].nValue/COIN);
1167 CCerror = "didnt find dicefinish tx";
1168 fprintf(stderr,"%s\n", CCerror.c_str());