]> Git Repo - VerusCoin.git/blob - src/cc/dice.cpp
Improve support for ID address differentiation and begin referral validation
[VerusCoin.git] / src / cc / dice.cpp
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"
17
18 // timeout
19
20 /*
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
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
27
28  2. and 3. can be done in mempool
29
30  The house commits to an entropy value by including the hash of the entropy value  in the 'E' transaction.
31
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
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
40 createfunding:
41  vins.*: normal inputs
42  vout.0: CC vout for funding
43  vout.1: owner vout
44  vout.2: dice marker address vout for easy searching
45  vout.3: normal change
46  vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
47
48 addfunding (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
55 bet:
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)
61  vout.3: change
62  vout.n-1: opreturn 'B' sbits fundingtxid entropy
63
64 loser:
65  vin.0: normal input
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
73
74 winner:
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
78
79 timeout:
80  same as winner, just without hentropy or proof
81
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.
86
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.
88  */
89
90 #include "../compat/endian.h"
91
92 static uint256 bettxids[128];
93
94 struct dicefinish_info
95 {
96     uint256 fundingtxid,bettxid;
97     uint64_t sbits;
98     int32_t iswin;
99 };
100
101 bool mySendrawtransaction(std::string res)
102 {
103     CTransaction tx; char str[65];
104     if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
105     {
106         if ( DecodeHexTx(tx,res) != 0 )
107         {
108             fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash()));
109             LOCK(cs_main);
110             if ( myAddtomempool(tx) != 0 )
111             {
112                 RelayTransaction(tx);
113                 fprintf(stderr,"added to mempool and broadcast\n");
114                 return(true);
115             } else fprintf(stderr,"error adding to mempool\n");
116         } else fprintf(stderr,"error decoding hex\n");
117     }
118     return(false);
119 }
120
121 void *dicefinish(void *_ptr)
122 {
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 )
128         {
129             duplicate = 1;
130             break;
131         }
132     if ( duplicate == 0 )
133     {
134         for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
135             if ( bettxids[i] == zeroid )
136             {
137                 bettxids[i] = ptr->bettxid;
138                 break;
139             }
140         if ( i == sizeof(bettxids)/sizeof(*bettxids) )
141             bettxids[rand() % i] = ptr->bettxid;
142     }
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 )
146     {
147         res = DiceBetFinish(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
148         if ( result > 0 )
149             mySendrawtransaction(res);
150     }
151     free(ptr);
152     return(0);
153 }
154
155 void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
156 {
157     struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
158     ptr->fundingtxid = fundingtxid;
159     ptr->bettxid = bettxid;
160     ptr->sbits = sbits;
161     ptr->iswin = iswin;
162     if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
163     {
164         //fprintf(stderr,"DiceQueue.%d\n",iswin);
165     } // small memory leak per DiceQueue
166 }
167
168 CPubKey DiceFundingPk(CScript scriptPubKey)
169 {
170     CPubKey pk; uint8_t *ptr,*dest; int32_t i;
171     if ( scriptPubKey.size() == 35 )
172     {
173         dest = (uint8_t *)pk.begin();
174         for (i=0; i<33; i++)
175             dest[i] = scriptPubKey[i+1];
176     } else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size());
177     return(pk);
178 }
179
180 uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
181 {
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());
187
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());
192
193     ssecret = curve25519(mypriv,txidpub);
194     ssecret2 = curve25519(txidpriv,mypub);
195     if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
196     {
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);
201     }
202     else
203     {
204         for (i=0; i<32; i++)
205             fprintf(stderr,"%02x",ssecret.bytes[i]);
206         fprintf(stderr," ssecret\n");
207         for (i=0; i<32; i++)
208             fprintf(stderr,"%02x",ssecret2.bytes[i]);
209         fprintf(stderr," ssecret2 dont match\n");
210     }
211     //char str[65],str2[65];
212     //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy));
213     return(hentropy);
214 }
215
216 int32_t dice_5nibbles(uint8_t *fivevals)
217 {
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]));
219 }
220
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)
222 {
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;
224     if ( odds < 10000 )
225         return(0);
226     else odds -= 10000;
227     if ( bet < minbet || bet > maxbet )
228     {
229         CCerror = strprintf("bet size violation %.8f",(double)bet/COIN);
230         fprintf(stderr,"%s\n", CCerror.c_str() );
231         return(0);
232     }
233     if ( odds > maxodds )
234     {
235         CCerror = strprintf("invalid odds %d, must be <= %d",odds, maxodds);
236         fprintf(stderr,"%s\n", CCerror.c_str() );
237         return(0);
238     }
239     //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,houseentropy),uint256_str(str2,bettorentropy));
240
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);
245
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);
250     winnings = 0;
251     if ( odds > 1 )
252     {
253         if ( 0 )
254         { // old way
255             bettor = (bettor / arith_uint256(odds));
256             if ( bettor >= house )
257                 winnings = bet * (odds+1);
258             return(winnings);
259         }
260         if ( odds > 9999 ) // shouldnt happen
261             return(0);
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);
266         for (i=0; i<32; i++)
267         {
268             hash16[i<<1] = ((hash[i] >> 4) & 0x0f);
269             hash16[(i<<1) + 1] = (hash[i] & 0x0f);
270         }
271         modval = 0;
272         for (i=0; i<12; i++)
273         {
274             modval = dice_5nibbles(&hash16[i*5]);
275             if ( modval < 1000000 )
276             {
277                 modval %= 10000;
278                 break;
279             }
280         }
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);
284     }
285     else if ( bettor >= house )
286         winnings = bet * (odds+1);
287     return(winnings);
288 }
289
290 CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
291 {
292     CScript opret; uint8_t evalcode = EVAL_DICE;
293     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
294     return(opret);
295 }
296
297 uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
298 {
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 )
303     {
304         if ( e == EVAL_DICE && f == 'F' )
305             return(f);
306     }
307     return(0);
308 }
309
310 CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
311 {
312     CScript opret; uint8_t evalcode = EVAL_DICE;
313     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
314     return(opret);
315 }
316
317 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
318 {
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 )
322     {
323         script = (uint8_t *)vopret.data();
324         if ( script[0] == EVAL_DICE )
325         {
326             if ( script[1] == 'F' )
327             {
328                 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
329                 {
330                     memset(&hash,0,32);
331                     fundingtxid = txid;
332                     return('F');
333                 } else fprintf(stderr,"unmarshal error for F\n");
334             }
335             else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
336             {
337                 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
338                     return(f);
339                 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
340             }
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());
343     return(0);
344 }
345
346 uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
347 {
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 )
350         return(hash);
351     else return(zeroid);
352 }
353
354 uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
355 {
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 )
358     {
359         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 && (numvouts= tx.vout.size()) > 0 )
360         {
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);
363         }
364     }
365     return(0);
366 }
367
368 int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t refsbits,uint256 reffundingtxid)
369 {
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++)
375     {
376         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
377         {
378             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
379                 return eval->Invalid("always should find vin, but didnt");
380             else
381             {
382                 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
383                     inputs += assetoshis;
384             }
385         }
386     }
387     for (i=0; i<numvouts; i++)
388     {
389         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
390         if ( (assetoshis= IsDicevout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
391             outputs += assetoshis;
392     }
393     return(inputs - outputs);
394 }
395
396 bool DiceIsmine(const CScript scriptPubKey)
397 {
398     char destaddr[64],myaddr[64];
399     Getscriptaddress(destaddr,scriptPubKey);
400     Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
401     return(strcmp(destaddr,myaddr) == 0);
402 }
403
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)
405 {
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 )
409     {
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 )
411         {
412             hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
413             if ( hentropy == hentropy2 )
414             {
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);
418                 if ( winnings == 0 )
419                 {
420                     // queue 'L' losing tx
421                     return(-1);
422                 }
423                 else
424                 {
425                     // queue 'W' winning tx
426                     return(1);
427                 }
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");
431     return(0);
432 }
433
434 bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
435 {
436     int32_t numblocks;
437     if ( CCduration(numblocks,betTx.GetHash()) <= 0 )
438         return(false);
439     return(numblocks >= timeoutblocks);
440 }
441
442 bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn)
443 {
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;
448     if ( numvouts < 1 )
449         return eval->Invalid("no vouts");
450     else
451     {
452         txid = tx.GetHash();
453         if ( (funcid=  DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
454         {
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;
462             switch ( funcid )
463             {
464                 case 'F':
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");
471                     break;
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");
479                     break;
480                 case 'B':
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)
486                     //vout.3: change
487                     //vout.n-1: opreturn 'B' sbits fundingtxid entropy
488                     preventCCvouts = 2;
489                     preventCCvins = 1;
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 )
509                     {
510                         if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
511                         {
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");
524                         }
525                     }
526                     if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
527                     {
528                         // will only happen for fundingPubKey
529                         DiceQueue(iswin,sbits,fundingtxid,txid);
530                     }
531                     break;
532                     // make sure all funding txid are from matching sbits and fundingtxid!!
533                 case 'L':
534                 case 'W':
535                 case 'T':
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
541                     preventCCvouts = 1;
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");
555                     if ( funcid == 'L' )
556                     {
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 )
562                         {
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");
565                         }
566                         iswin = -1;
567                     }
568                     else
569                     {
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) )
581                         {
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");
584                         }
585                         else if ( tx.vout[3].scriptPubKey != fundingPubKey )
586                         {
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");
589                         }
590                         iswin = (funcid == 'W');
591                     }
592                     if ( iswin != 0 )
593                     {
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");
602                     }
603                     else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 )
604                         return eval->Invalid("invalid timeout claim for timeout");
605                     break;
606             }
607         }
608         return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
609     }
610     return(true);
611 }
612
613 uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
614 {
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++)
620     {
621         txid = it->first.txhash;
622         vout = (int32_t)it->first.index;
623         if ( it->second.satoshis < 1000000 )
624             continue;
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 )
628                 break;
629         if ( j != mtx.vin.size() )
630             continue;
631         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
632         {
633             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
634             {
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 )
639                 {
640                     if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
641                     {
642                         if ( total != 0 && maxinputs != 0 )
643                             mtx.vin.push_back(CTxIn(txid,vout,CScript()));
644                         totalinputs += it->second.satoshis;
645                         n++;
646                         if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
647                             break;
648                     }
649                 }
650             } else fprintf(stderr,"null funcid\n");
651         }
652     }
653     return(totalinputs);
654 }
655
656 int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid)
657 {
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;
660     entropyval = 0;
661     entropytxid = zeroid;
662     if ( GetTransaction(reffundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 )
663     {
664         fundingPubKey = tx.vout[1].scriptPubKey;
665     } else return(0);
666     GetCCaddress(cp,coinaddr,dicepk);
667     SetCCunspents(unspentOutputs,coinaddr);
668     entropyval = 0;
669     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
670     {
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 )
674         {
675             //char str[65],str2[65];
676             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
677             {
678                 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
679                 {
680                     if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T')  )
681                     {
682                         fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN);
683                         if ( funcid != 'F' && funcid != 'T' )
684                             n++;
685                         totalinputs += nValue;
686                         if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
687                         {
688                             fprintf(stderr,"check first\n");
689                             if ( fundingPubKey == tx.vout[1].scriptPubKey )
690                             {
691                                 if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash )
692                                 {
693                                     if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 )
694                                     {
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());
696                                         continue;
697                                     }
698                                     if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
699                                     {
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));
708                                         continue;
709                                     }
710                                 } //else fprintf(stderr,"not E or is funding\n");
711                                 entropytxid = txid;
712                                 entropyval = tx.vout[0].nValue;
713                                 first = 1;
714                             }
715                             else
716                             {
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));
725                             }
726                         }
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);
730         }
731     }
732     fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN);
733     return(totalinputs);
734 }
735
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)
737 {
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
743     {
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 )
746         {
747             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
748             {
749                 fundingPubKey = tx.vout[1].scriptPubKey;
750                 return(true);
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");
753         return(false);
754     }
755     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
756     {
757         //int height = it->first.blockHeight;
758         txid = it->first.txhash;
759         if ( fundingtxid != zeroid && txid != fundingtxid )
760             continue;
761         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
762         {
763             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
764             {
765                 if ( sbits == refsbits )
766                 {
767                     fundingPubKey = tx.vout[1].scriptPubKey;
768                     fundingtxid = txid;
769                     return(true);
770                 }
771             }
772         }
773     }
774     return(false);
775 }
776
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)
778 {
779     struct CCcontract_info *cp; int32_t cmpflag;
780     cp = CCinit(C,EVAL_DICE);
781     if ( txfee == 0 )
782         txfee = 10000;
783     mypk = pubkey2pk(Mypubkey());
784     dicepk = GetUnspendable(cp,0);
785     sbits = stringbits(planstr);
786     if ( reffundingtxid == zeroid )
787         cmpflag = 0;
788     else cmpflag = 1;
789     if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
790     {
791         fprintf(stderr,"Dice plan (%s) exists.%d vs cmpflag.%d\n",planstr,!cmpflag,cmpflag);
792         return(0);
793     }
794     return(cp);
795 }
796
797 UniValue DiceInfo(uint256 diceid)
798 {
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 )
801     {
802         fprintf(stderr,"cant find fundingtxid\n");
803         ERR_RESULT("cant find fundingtxid");
804         return(result);
805     }
806     if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
807     {
808         fprintf(stderr,"fundingtxid isnt dice creation txid\n");
809         ERR_RESULT("fundingtxid isnt dice creation txid");
810         return(result);
811     }
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));
828     return(result);
829 }
830
831 UniValue DiceList()
832 {
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++)
837     {
838         txid = it->first.txhash;
839         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
840         {
841             if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
842             {
843                 result.push_back(uint256_str(str,txid));
844             }
845         }
846     }
847     return(result);
848 }
849
850 std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
851 {
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 )
854     {
855         CCerror = "invalid parameter error";
856         fprintf(stderr,"%s\n", CCerror.c_str() );
857         return("");
858     }
859     if ( funds < 100*COIN )
860     {
861         CCerror = "dice plan needs at least 100 coins";
862         fprintf(stderr,"%s\n", CCerror.c_str() );
863         return("");
864     }
865     memset(&zero,0,sizeof(zero));
866     if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
867     {
868         CCerror = "Diceinit error in create funding";
869         fprintf(stderr,"%s\n", CCerror.c_str() );
870         return("");
871     }
872     if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 )
873     {
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)));
878     }
879     CCerror = "cant find enough inputs";
880     fprintf(stderr,"%s\n", CCerror.c_str() );
881     return("");
882 }
883
884 std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
885 {
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;
887     if ( amount < 0 )
888     {
889         CCerror = "amount must be positive";
890         fprintf(stderr,"%s\n", CCerror.c_str() );
891         return("");
892     }
893     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
894         return("");
895     scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
896     if ( 0 )
897     {
898         uint8_t *ptr0,*ptr1; int32_t i;
899         for (i=0; i<35; i++)
900             fprintf(stderr,"%02x",scriptPubKey[i]);
901         fprintf(stderr," script vs ");
902         for (i=0; i<35; i++)
903             fprintf(stderr,"%02x",fundingPubKey[i]);
904         fprintf(stderr," funding\n");
905     }
906     if ( scriptPubKey == fundingPubKey )
907     {
908         if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 )
909         {
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)));
914         } else {
915             CCerror = "cant find enough inputs";
916             fprintf(stderr,"%s\n", CCerror.c_str() );
917         }
918     } else {
919         CCerror = "only fund creator can add more funds (entropy)";
920         fprintf(stderr,"%s\n", CCerror.c_str() );
921     }
922     return("");
923 }
924
925 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
926 {
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;
928     if ( bet < 0 )
929     {
930         CCerror = "bet must be positive";
931         fprintf(stderr,"%s\n", CCerror.c_str() );
932         return("");
933     }
934     if ( odds < 1 || odds > 9999 )
935     {
936         CCerror = "odds must be between 1 and 9999";
937         fprintf(stderr,"%s\n", CCerror.c_str() );
938         return("");
939     }
940     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
941         return("");
942     if ( bet < minbet || bet > maxbet || odds > maxodds )
943     {
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() );
946         return("");
947     }
948     if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
949     {
950         if ( myIsutxo_spentinmempool(entropytxid,0) != 0 )
951         {
952             CCerror = "entropy txid is spent";
953             fprintf(stderr,"%s\n", CCerror.c_str() );
954             return("");
955         }
956         mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
957         if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
958         {
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);
965     }
966     if ( entropyval == 0 && funding != 0 )
967         CCerror = "cant find dice entropy inputs";
968     else
969         CCerror = "cant find dice input";
970     fprintf(stderr,"%s\n", CCerror.c_str() );
971     return("");
972 }
973
974 std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
975 {
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;
977     *resultp = 0;
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 )
980     {
981         CCerror = "Diceinit error in finish";
982         fprintf(stderr,"%s\n", CCerror.c_str() );
983         return("");
984     }
985     fundingpk = DiceFundingPk(fundingPubKey);
986     if ( winlosetimeout != 0 )
987     {
988         scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
989         if ( scriptPubKey != fundingPubKey )
990         {
991             //fprintf(stderr,"only dice fund creator can submit winner or loser\n");
992             winlosetimeout = 0;
993         }
994     }
995     if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 )
996     {
997         CCerror = "no txfee inputs for win/lose";
998         fprintf(stderr,"%s\n", CCerror.c_str() );
999         return("");
1000     }
1001     if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
1002     {
1003         bettorentropy = DiceGetEntropy(betTx,'B');
1004         if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1005         {
1006             if ( winlosetimeout != 0 )
1007                 winlosetimeout = iswin;
1008             if ( iswin == winlosetimeout )
1009             {
1010                 if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 )
1011                 {
1012                     CCerror = "bettxid already spent";
1013                     fprintf(stderr,"%s\n", CCerror.c_str() );
1014                     return("");
1015                 }
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()));
1019                 if ( iswin == 0 )
1020                 {
1021                     funcid = 'T';
1022                     if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
1023                     {
1024                         return("");
1025                     }
1026                     else
1027                     {
1028                         hentropy = hentropyproof = zeroid;
1029                         iswin = 1;
1030                     }
1031                 }
1032                 if ( iswin > 0 )
1033                 {
1034                     if ( funcid != 'T' )
1035                         funcid = 'W';
1036                     odds = (betTx.vout[2].nValue - txfee);
1037                     if ( odds < 1 || odds > maxodds )
1038                     {
1039                         CCerror = strprintf("illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
1040                         fprintf(stderr,"%s\n", CCerror.c_str() );
1041                         return("");
1042                     }
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 )
1048                     {
1049                         if ( inputs > fundsneeded )
1050                             CCchange += (inputs - fundsneeded);
1051                     }
1052                     else
1053                     {
1054                         CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN);
1055                         fprintf(stderr,"%s\n", CCerror.c_str() );
1056                         return("");
1057                     }
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));
1061                 }
1062                 else
1063                 {
1064                     funcid = 'L';
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));
1067                 }
1068                 if ( winlosetimeout != 0 )
1069                     hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
1070                 *resultp = 1;
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);
1075         }
1076         else
1077         {
1078             *resultp = -1;
1079             fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
1080             return("");
1081         }
1082     }
1083     *resultp = -1;
1084     CCerror = "couldnt find bettx or entropytx";
1085     fprintf(stderr,"%s\n", CCerror.c_str() );
1086     return("");
1087 }
1088
1089 double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid)
1090 {
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 )
1093     {
1094         CCerror = "Diceinit error in status";
1095         fprintf(stderr,"%s\n", CCerror.c_str() );
1096         return(0.);
1097     }
1098     fundingpk = DiceFundingPk(fundingPubKey);
1099     scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
1100     GetCCaddress(cp,coinaddr,dicepk);
1101     if ( bettxid == zeroid ) // scan
1102     {
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++)
1106         {
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 )
1110             {
1111                 if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' )
1112                 {
1113                     res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey);
1114                     if ( result > 0 )
1115                     {
1116                         mySendrawtransaction(res);
1117                         n++;
1118                     }
1119                 }
1120             }
1121         }
1122         if ( 0 && scriptPubKey == fundingPubKey )
1123         {
1124             for (i=0; i<=n; i++)
1125             {
1126                 res = DiceAddfunding(txfee,planstr,fundingtxid,COIN);
1127                 fprintf(stderr,"ENTROPY tx:\n");
1128                 mySendrawtransaction(res);
1129             }
1130         }
1131         return(n);
1132     }
1133     else
1134     {
1135         char str[65];
1136         if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1137         {
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 )
1140             {
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 )
1143                     return(0.);
1144                 else return((double)spenttx.vout[2].nValue/COIN);
1145             }
1146             CCerror = "couldnt find bettx or spenttx %s\n",uint256_str(str,spenttxid);
1147             fprintf(stderr,"%s\n", CCerror.c_str());
1148             return(-1.);
1149         }
1150         else if ( scriptPubKey == fundingPubKey )
1151             res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,1);
1152         else res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,0);
1153         if ( result > 0 )
1154         {
1155             mySendrawtransaction(res);
1156             sleep(1);
1157             if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1158             {
1159                 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() >= 2 )
1160                 {
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 )
1163                         return(0.);
1164                     else return((double)spenttx.vout[2].nValue/COIN);
1165                 } else return(0.);
1166             }
1167             CCerror = "didnt find dicefinish tx";
1168             fprintf(stderr,"%s\n", CCerror.c_str());
1169         }
1170         return(-1.);
1171     }
1172     return(0.);
1173 }
This page took 0.094073 seconds and 4 git commands to generate.