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
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
65 vin.0: betTx CC vout.0 entropy from bet
66 vin.1: betTx CC vout.1 bet amount from bet
67 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
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
74 vin.0: betTx CC vout.0 entropy from bet
75 vin.1: betTx CC vout.1 bet amount from bet
76 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
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
82 vin.0: betTx CC vout.0 entropy from bet
83 vin.1: betTx CC vout.1 bet amount from bet
84 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
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
88 vout.n-1: opreturn 'T' sbits fundingtxid hentropy
93 #include "../endian.h"
95 static uint256 bettxids[128];
97 struct dicefinish_info
99 uint256 fundingtxid,bettxid;
104 void *dicefinish(void *_ptr)
106 char str[65],str2[65],name[32]; std::string res; int32_t i,duplicate=0; struct dicefinish_info *ptr;
107 ptr = (struct dicefinish_info *)_ptr;
109 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
110 if ( bettxids[i] == ptr->bettxid )
115 if ( duplicate == 0 )
117 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
118 if ( bettxids[i] == zeroid )
120 bettxids[i] = ptr->bettxid;
123 if ( i == sizeof(bettxids)/sizeof(*bettxids) )
124 bettxids[rand() % i] = ptr->bettxid;
126 unstringbits(name,ptr->sbits);
127 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));
128 if ( duplicate == 0 )
130 CTransaction tx; uint256 txid; char str[65]; int32_t result;
131 res = DiceWinLoseTimeout(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
132 if ( result != 0 && res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
135 if ( DecodeHexTx(tx,res) != 0 )
138 RelayTransaction(tx);
139 fprintf(stderr,"%s\nresult.(%s)\n",res.c_str(),uint256_str(str,txid));
147 void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
149 struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
150 ptr->fundingtxid = fundingtxid;
151 ptr->bettxid = bettxid;
154 if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
156 fprintf(stderr,"DiceQueue.%d\n",iswin);
157 } // small memory leak per DiceQueue
160 void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
163 #if defined(WORDS_BIGENDIAN)
164 for (i=31; i>=0; i--)
167 memcpy(dest,src,len);
171 uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
173 int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
174 memset(&hentropy,0,32);
175 endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
176 txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
177 txidpub = curve25519(txidpriv,curve25519_basepoint9());
179 Myprivkey(tmp256.bytes);
180 vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
181 mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
182 mypub = curve25519(mypriv,curve25519_basepoint9());
184 ssecret = curve25519(mypriv,txidpub);
185 ssecret2 = curve25519(txidpriv,mypub);
186 if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
188 vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
189 vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
190 endiancpy((uint8_t *)&entropy,_entropy,32);
191 endiancpy((uint8_t *)&hentropy,_hentropy,32);
196 fprintf(stderr,"%02x",ssecret.bytes[i]);
197 fprintf(stderr," ssecret\n");
199 fprintf(stderr,"%02x",ssecret2.bytes[i]);
200 fprintf(stderr," ssecret2 dont match\n");
205 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)
207 uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
211 if ( bet < minbet || bet > maxbet || odds > maxodds )
213 fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
216 endiancpy(buf,(uint8_t *)&houseentropy,32);
217 endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
218 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
219 endiancpy((uint8_t *)&house,_house,32);
221 endiancpy(buf,(uint8_t *)&bettorentropy,32);
222 endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
223 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
224 endiancpy((uint8_t *)&bettor,_bettor,32);
226 bettor = (bettor / arith_uint256(odds));
227 if ( bettor >= house )
228 winnings = bet * odds;
230 fprintf(stderr,"winnings %.8f bet %.8f at odds %d:1 %s vs %s\n",(double)winnings/COIN,(double)bet/COIN,(int32_t)odds,uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
234 CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
236 CScript opret; uint8_t evalcode = EVAL_DICE;
237 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
241 uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
243 std::vector<uint8_t> vopret; uint8_t *script,e,f;
244 GetOpReturnData(scriptPubKey, vopret);
245 script = (uint8_t *)vopret.data();
246 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
248 if ( e == EVAL_DICE && f == 'F' )
254 CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
256 CScript opret; uint8_t evalcode = EVAL_DICE;
257 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
261 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
263 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
264 GetOpReturnData(scriptPubKey,vopret);
265 if ( vopret.size() > 2 )
267 script = (uint8_t *)vopret.data();
268 if ( script[0] == EVAL_DICE )
270 if ( script[1] == 'F' )
272 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
277 } else fprintf(stderr,"unmarshal error for F\n");
279 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
281 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
283 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
285 } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
286 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
290 uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
292 uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
293 if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
298 uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
301 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
303 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
304 return(tx.vout[v].nValue);
309 bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
311 static uint256 zerohash;
312 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
313 numvins = tx.vin.size();
314 numvouts = tx.vout.size();
315 for (i=0; i<numvins; i++)
317 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
319 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
320 return eval->Invalid("always should find vin, but didnt");
323 if ( hashBlock == zerohash )
324 return eval->Invalid("cant dice from mempool");
325 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
326 inputs += assetoshis;
330 for (i=0; i<numvouts; i++)
332 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
333 if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
334 outputs += assetoshis;
336 if ( inputs != outputs+txfee )
338 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
339 return eval->Invalid("mismatched inputs != outputs + txfee");
344 bool DiceIsmine(const CScript scriptPubKey)
346 char destaddr[64],myaddr[64];
347 Getscriptaddress(destaddr,scriptPubKey);
348 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
349 return(strcmp(destaddr,myaddr) == 0);
352 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)
354 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
355 //char str[65],str2[65];
356 if ( DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout.size() > 0 )
358 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 )
360 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
361 if ( hentropy == hentropy2 )
363 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
364 //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);
367 // queue 'L' losing tx
372 // queue 'W' winning tx
381 bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
383 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;
384 numvins = tx.vin.size();
385 numvouts = tx.vout.size();
386 preventCCvins = preventCCvouts = -1;
388 return eval->Invalid("no vouts");
392 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
394 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
395 return eval->Invalid("cant find fundingtxid");
396 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
397 return eval->Invalid("fundingTx not valid");
401 //vins.*: normal inputs
402 //vout.0: CC vout for funding
403 //vout.1: normal marker vout for easy searching
404 //vout.2: normal change
405 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
406 return eval->Invalid("unexpected DiceValidate for createfunding");
408 case 'E': // check sig of vin to match fundingtxid
409 //vins.*: normal inputs
410 //vout.0: CC vout for locked entropy funds
411 //vout.1: tag to owner address for entropy funds
412 //vout.2: normal change
413 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
414 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
417 //vin.0: entropy txid from house
418 //vins.1+: normal inputs
419 //vout.0: CC vout for locked entropy
420 //vout.1: CC vout for locked bet
421 //vout.2: tag for bettor's address (txfee + odds)
423 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
424 if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
425 return eval->Invalid("always should find vin.0, but didnt");
426 if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
428 fprintf(stderr,"DiceIsWinner.%d\n",iswin);
429 DiceQueue(iswin,sbits,fundingtxid,txid);
431 //return eval->Invalid("dont confirm bet during debug");
434 //vin.0: betTx CC vout.0 entropy from bet
435 //vin.1: betTx CC vout.1 bet amount from bet
436 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
437 //vout.0: funding CC to entropy owner
438 //vout.1: tag to owner address for entropy funds
439 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy
442 //vin.0: betTx CC vout.0 entropy from bet
443 //vin.1: betTx CC vout.1 bet amount from bet
444 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
445 //vout.0: funding CC change to entropy owner
446 //vout.1: tag to owner address for entropy funds
447 //vout.2: normal output to bettor's address
448 //vout.n-1: opreturn 'W' sbits fundingtxid hentropy
451 //vin.0: betTx CC vout.0 entropy from bet
452 //vin.1: betTx CC vout.1 bet amount from bet
453 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
454 //vout.0: funding CC vin.0 to entropy owner
455 //vout.1: tag to owner address for entropy funds
456 //vout.2: normal vin.1 to bettor's address
457 //vout.n-1: opreturn 'T' sbits fundingtxid hentropy
458 /*for (i=0; i<numvins; i++)
460 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
461 return eval->Invalid("unexpected normal vin for unlock");
463 if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
465 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
466 return eval->Invalid("always should find vin.0, but didnt");
467 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
468 return eval->Invalid("lock tx vout.0 is normal output");
469 else if ( tx.vout.size() < 3 )
470 return eval->Invalid("unlock tx not enough vouts");
471 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
472 return eval->Invalid("unlock tx vout.0 is normal output");
473 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
474 return eval->Invalid("unlock tx vout.1 is CC output");
475 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
476 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
477 amount = vinTx.vout[0].nValue;
479 //reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,timeoutblocks);
480 if ( tx.vout[1].nValue > amount+reward )
481 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
482 preventCCvouts = 1;*/
486 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
491 uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
493 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;
494 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
495 GetCCaddress(cp,coinaddr,pk);
496 SetCCunspents(unspentOutputs,coinaddr);
497 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
499 txid = it->first.txhash;
500 vout = (int32_t)it->first.index;
501 if ( it->second.satoshis < 1000000 )
503 fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
504 for (j=0; j<mtx.vin.size(); j++)
505 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
507 if ( j != mtx.vin.size() )
509 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
511 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
513 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
514 if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'W' && funcid != 'L' && funcid != 'T' )
516 else if ( fundsflag == 0 && funcid != 'B' )
518 if ( total != 0 && maxinputs != 0 )
520 if ( fundsflag == 0 )
521 scriptPubKey = tx.vout[1].scriptPubKey;
522 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
524 totalinputs += it->second.satoshis;
526 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
528 } else fprintf(stderr,"null funcid\n");
534 uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
536 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;
537 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
538 GetCCaddress(cp,coinaddr,pk);
539 SetCCunspents(unspentOutputs,coinaddr);
541 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
543 txid = it->first.txhash;
544 vout = (int32_t)it->first.index;
545 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
547 char str[65],str2[65];
548 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
550 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
552 if ( funcid != 'B' && refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 )
554 totalinputs += nValue;
555 fprintf(stderr,"add %.8f\n",(double)nValue/COIN);
556 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
559 entropyval = tx.vout[0].nValue;
562 } //else fprintf(stderr,"%c refsbits.%llx sbits.%llx nValue %.8f\n",funcid,(long long)refsbits,(long long)sbits,(double)nValue/COIN);
563 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
564 } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
570 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)
572 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
573 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
574 GetCCaddress(cp,CCaddr,dicepk);
575 SetCCtxids(txids,cp->normaladdr);
576 if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
578 if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
580 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
582 fundingPubKey = tx.vout[1].scriptPubKey;
588 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
590 //int height = it->first.blockHeight;
591 txid = it->first.txhash;
592 if ( fundingtxid != zeroid && txid != fundingtxid )
594 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
596 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
598 if ( sbits == refsbits )
600 fundingPubKey = tx.vout[1].scriptPubKey;
610 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)
612 struct CCcontract_info *cp; int32_t cmpflag;
613 cp = CCinit(C,EVAL_DICE);
616 mypk = pubkey2pk(Mypubkey());
617 dicepk = GetUnspendable(cp,0);
618 sbits = stringbits(planstr);
619 if ( reffundingtxid == zeroid )
622 if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
624 fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
630 UniValue DiceInfo(uint256 diceid)
632 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;
633 if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
635 fprintf(stderr,"cant find fundingtxid\n");
636 result.push_back(Pair("error","cant find fundingtxid"));
639 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
641 fprintf(stderr,"fundingtxid isnt dice creation txid\n");
642 result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
645 result.push_back(Pair("result","success"));
646 result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
647 unstringbits(str,sbits);
648 result.push_back(Pair("name",str));
649 result.push_back(Pair("sbits",sbits));
650 sprintf(numstr,"%.8f",(double)minbet/COIN);
651 result.push_back(Pair("minbet",numstr));
652 sprintf(numstr,"%.8f",(double)maxbet/COIN);
653 result.push_back(Pair("maxbet",numstr));
654 result.push_back(Pair("maxodds",maxodds));
655 result.push_back(Pair("timeoutblocks",timeoutblocks));
656 cp = CCinit(&C,EVAL_DICE);
657 dicepk = GetUnspendable(cp,0);
658 funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
659 sprintf(numstr,"%.8f",(double)funding/COIN);
660 result.push_back(Pair("funding",numstr));
666 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];
667 cp = CCinit(&C,EVAL_DICE);
668 SetCCtxids(addressIndex,cp->normaladdr);
669 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
671 txid = it->first.txhash;
672 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
674 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
676 result.push_back(uint256_str(str,txid));
683 std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
685 CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
686 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
688 fprintf(stderr,"negative parameter error\n");
691 memset(&zero,0,sizeof(zero));
692 if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
694 if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
696 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
697 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
698 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
699 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
701 fprintf(stderr,"cant find enough inputs\n");
705 std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
707 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;
710 fprintf(stderr,"negative parameter error\n");
713 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
715 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
718 uint8_t *ptr0,*ptr1; int32_t i;
719 ptr0 = (uint8_t *)scriptPubKey.data();
720 ptr1 = (uint8_t *)fundingPubKey.data();
722 fprintf(stderr,"%02x",ptr0[i]);
723 fprintf(stderr," script vs ");
725 fprintf(stderr,"%02x",ptr1[i]);
726 fprintf(stderr," funding\n");
728 if ( scriptPubKey == fundingPubKey )
730 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 )
732 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
733 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
734 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
735 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
736 } else fprintf(stderr,"cant find enough inputs\n");
737 } else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
741 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
743 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;
744 if ( bet < 0 || odds < 1 )
746 fprintf(stderr,"negative parameter error\n");
749 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
751 if ( bet < minbet || bet > maxbet || odds > maxodds )
753 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);
756 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
758 mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
759 if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
761 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
762 mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
763 mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
764 mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
765 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
766 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
768 if ( entropyval == 0 && funding != 0 )
769 fprintf(stderr,"cant find dice entropy inputs\n");
770 else fprintf(stderr,"cant find dice inputs\n");
774 std::string DiceWinLoseTimeout(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
776 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;
778 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
780 if ( winlosetimeout != 0 )
782 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
783 if ( scriptPubKey != fundingPubKey )
785 fprintf(stderr,"only dice fund creator can submit winner or loser\n");
789 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
791 bettorentropy = DiceGetEntropy(betTx,'B');
792 if ( (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
794 if ( iswin == winlosetimeout )
796 fprintf(stderr,"iswin.%d matches\n",iswin);
797 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
798 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
802 fprintf(stderr,"timeout refunds are not supported yet\n");
804 else if ( iswin > 0 )
807 odds = (betTx.vout[2].nValue - txfee);
808 if ( odds < 1 || odds > maxodds )
810 fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
813 CCchange = betTx.vout[0].nValue;
814 fundsneeded = 2*txfee + (odds-1)*betTx.vout[1].nValue;
815 if ( (inputs= AddDiceInputs(scriptPubKey,1,cp,mtx,dicepk,fundsneeded,60)) > 0 )
817 if ( inputs > fundsneeded+txfee )
818 CCchange += (inputs - (fundsneeded+txfee));
819 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
820 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
821 mtx.vout.push_back(CTxOut(odds * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
825 fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
832 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue - txfee,dicepk));
833 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
835 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
837 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
838 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);