]> Git Repo - VerusCoin.git/blob - src/cc/dice.cpp
Test
[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  */
83
84 #include "../endian.h"
85
86 static uint256 bettxids[128];
87
88 struct dicefinish_info
89 {
90     uint256 fundingtxid,bettxid;
91     uint64_t sbits;
92     int32_t iswin;
93 };
94
95 bool mySendrawtransaction(std::string res)
96 {
97     CTransaction tx; char str[65];
98     if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
99     {
100         if ( DecodeHexTx(tx,res) != 0 )
101         {
102             fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash()));
103             LOCK(cs_main);
104             if ( myAddtomempool(tx) != 0 )
105             {
106                 RelayTransaction(tx);
107                 fprintf(stderr,"added to mempool and broadcast\n");
108                 return(true);
109             } else fprintf(stderr,"error adding to mempool\n");
110         } else fprintf(stderr,"error decoding hex\n");
111     }
112     return(false);
113 }
114
115 void *dicefinish(void *_ptr)
116 {
117     char str[65],str2[65],name[32]; std::string res; int32_t i,result,duplicate=0; struct dicefinish_info *ptr;
118     ptr = (struct dicefinish_info *)_ptr;
119     sleep(3); // wait for bettxid to be in mempool
120     for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
121         if ( bettxids[i] == ptr->bettxid )
122         {
123             duplicate = 1;
124             break;
125         }
126     if ( duplicate == 0 )
127     {
128         for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
129             if ( bettxids[i] == zeroid )
130             {
131                 bettxids[i] = ptr->bettxid;
132                 break;
133             }
134         if ( i == sizeof(bettxids)/sizeof(*bettxids) )
135             bettxids[rand() % i] = ptr->bettxid;
136     }
137     unstringbits(name,ptr->sbits);
138     //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));
139     if ( duplicate == 0 )
140     {
141         res = DiceBetFinish(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
142         if ( result > 0 )
143             mySendrawtransaction(res);
144     }
145     free(ptr);
146     return(0);
147 }
148
149 void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
150 {
151     struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
152     ptr->fundingtxid = fundingtxid;
153     ptr->bettxid = bettxid;
154     ptr->sbits = sbits;
155     ptr->iswin = iswin;
156     if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
157     {
158         //fprintf(stderr,"DiceQueue.%d\n",iswin);
159     } // small memory leak per DiceQueue
160 }
161
162 void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
163 {
164     int32_t i,j=0;
165 #if defined(WORDS_BIGENDIAN)
166     for (i=31; i>=0; i--)
167         dest[j++] = src[i];
168 #else
169     memcpy(dest,src,len);
170 #endif
171 }
172
173 CPubKey DiceFundingPk(CScript scriptPubKey)
174 {
175     CPubKey pk; uint8_t *ptr,*dest; int32_t i;
176     if ( scriptPubKey.size() == 35 )
177     {
178         ptr = (uint8_t *)scriptPubKey.data();
179         dest = (uint8_t *)pk.begin();
180         for (i=0; i<33; i++)
181             dest[i] = ptr[i+1];
182     } else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size());
183     return(pk);
184 }
185
186 uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
187 {
188     int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
189     memset(&hentropy,0,32);
190     endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
191     txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
192     txidpub = curve25519(txidpriv,curve25519_basepoint9());
193
194     Myprivkey(tmp256.bytes);
195     vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
196     mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
197     mypub = curve25519(mypriv,curve25519_basepoint9());
198
199     ssecret = curve25519(mypriv,txidpub);
200     ssecret2 = curve25519(txidpriv,mypub);
201     if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
202     {
203         vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
204         vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
205         endiancpy((uint8_t *)&entropy,_entropy,32);
206         endiancpy((uint8_t *)&hentropy,_hentropy,32);
207     }
208     else
209     {
210         for (i=0; i<32; i++)
211             fprintf(stderr,"%02x",ssecret.bytes[i]);
212         fprintf(stderr," ssecret\n");
213         for (i=0; i<32; i++)
214             fprintf(stderr,"%02x",ssecret2.bytes[i]);
215         fprintf(stderr," ssecret2 dont match\n");
216     }
217     //char str[65],str2[65];
218     //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy));
219     return(hentropy);
220 }
221
222 uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
223 {
224     uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
225     if ( odds < 10000 )
226         return(0);
227     else odds -= 10000;
228     if ( bet < minbet || bet > maxbet || odds > maxodds )
229     {
230         fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
231         return(0);
232     }
233     //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,houseentropy),uint256_str(str2,bettorentropy));
234
235     endiancpy(buf,(uint8_t *)&houseentropy,32);
236     endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
237     vcalc_sha256(0,(uint8_t *)&_house,buf,64);
238     endiancpy((uint8_t *)&house,_house,32);
239
240     endiancpy(buf,(uint8_t *)&bettorentropy,32);
241     endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
242     vcalc_sha256(0,(uint8_t *)&_bettor,buf,64);
243     endiancpy((uint8_t *)&bettor,_bettor,32);
244     if ( odds > 1 )
245         bettor = (bettor / arith_uint256(odds));
246     if ( bettor >= house )
247         winnings = bet * (odds+1);
248     else winnings = 0;
249     return(winnings);
250 }
251
252 CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
253 {
254     CScript opret; uint8_t evalcode = EVAL_DICE;
255     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
256     return(opret);
257 }
258
259 uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
260 {
261     std::vector<uint8_t> vopret; uint8_t *script,e,f;
262     GetOpReturnData(scriptPubKey, vopret);
263     script = (uint8_t *)vopret.data();
264     if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
265     {
266         if ( e == EVAL_DICE && f == 'F' )
267             return(f);
268     }
269     return(0);
270 }
271
272 CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
273 {
274     CScript opret; uint8_t evalcode = EVAL_DICE;
275     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
276     return(opret);
277 }
278
279 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
280 {
281     std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
282     GetOpReturnData(scriptPubKey,vopret);
283     if ( vopret.size() > 2 )
284     {
285         script = (uint8_t *)vopret.data();
286         if ( script[0] == EVAL_DICE )
287         {
288             if ( script[1] == 'F' )
289             {
290                 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
291                 {
292                     memset(&hash,0,32);
293                     fundingtxid = txid;
294                     return('F');
295                 } else fprintf(stderr,"unmarshal error for F\n");
296             }
297             else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
298             {
299                 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
300                     return(f);
301                 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
302             }
303         } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
304     } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
305     return(0);
306 }
307
308 uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
309 {
310     uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
311     if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
312         return(hash);
313     else return(zeroid);
314 }
315
316 uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
317 {
318     char destaddr[64];
319     if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
320     {
321         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
322             return(tx.vout[v].nValue);
323     }
324     return(0);
325 }
326
327 int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
328 {
329     CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis;
330     numvins = tx.vin.size();
331     numvouts = tx.vout.size();
332     inputs = outputs = 0;
333     for (i=0; i<numvins; i++)
334     {
335         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
336         {
337             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
338                 return eval->Invalid("always should find vin, but didnt");
339             else
340             {
341                 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
342                     inputs += assetoshis;
343             }
344         }
345     }
346     for (i=0; i<numvouts; i++)
347     {
348         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
349         if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
350             outputs += assetoshis;
351     }
352     return(inputs - outputs);
353 }
354
355 bool DiceIsmine(const CScript scriptPubKey)
356 {
357     char destaddr[64],myaddr[64];
358     Getscriptaddress(destaddr,scriptPubKey);
359     Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
360     return(strcmp(destaddr,myaddr) == 0);
361 }
362
363 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)
364 {
365     uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
366     //char str[65],str2[65];
367     if ( vinTx.vout.size() > 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
368     {
369         if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L') && sbits == vinsbits && fundingtxid == vinfundingtxid )
370         {
371             hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
372             if ( hentropy == hentropy2 )
373             {
374                 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
375                 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));
376                 //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);
377                 if ( winnings == 0 )
378                 {
379                     // queue 'L' losing tx
380                     return(-1);
381                 }
382                 else
383                 {
384                     // queue 'W' winning tx
385                     return(1);
386                 }
387             } else fprintf(stderr,"hentropy != hentropy2\n");
388         } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid);
389     } //else fprintf(stderr,"notmine or not CC\n");
390     return(0);
391 }
392
393 bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
394 {
395     fprintf(stderr,"DiceVerifyTimeout needs to be implemented\n");
396     return(false);
397 }
398
399 bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
400 {
401     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];
402     numvins = tx.vin.size();
403     numvouts = tx.vout.size();
404     preventCCvins = preventCCvouts = -1;
405     if ( numvouts < 1 )
406         return eval->Invalid("no vouts");
407     else
408     {
409         txid = tx.GetHash();
410         if ( (funcid=  DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
411         {
412             if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
413                 return eval->Invalid("cant find fundingtxid");
414             else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
415                 return eval->Invalid("fundingTx not valid");
416             fundingPubKey = fundingTx.vout[1].scriptPubKey;
417             switch ( funcid )
418             {
419                 case 'F':
420                     //vins.*: normal inputs
421                     //vout.0: CC vout for funding
422                     //vout.1: normal marker vout for easy searching
423                     //vout.2: normal change
424                     //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
425                     return eval->Invalid("unexpected DiceValidate for createfunding");
426                     break;
427                 case 'E': // check sig of vin to match fundingtxid in the 'B' tx
428                     //vins.*: normal inputs
429                     //vout.0: CC vout for locked entropy funds
430                     //vout.1: tag to owner address for entropy funds
431                     //vout.2: normal change
432                     //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
433                     return eval->Invalid("unexpected DiceValidate for addfunding entropy");
434                     break;
435                 case 'B':
436                     //vin.0: entropy txid from house
437                     //vins.1+: normal inputs
438                     //vout.0: CC vout for locked entropy
439                     //vout.1: CC vout for locked bet
440                     //vout.2: tag for bettor's address (txfee + odds)
441                     //vout.3: change
442                     //vout.n-1: opreturn 'B' sbits fundingtxid entropy
443                     preventCCvouts = 2;
444                     preventCCvins = 1;
445                     if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
446                         return eval->Invalid("vin.0 is normal for bet");
447                     else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
448                         return eval->Invalid("vout.0 is normal for bet");
449                     else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
450                         return eval->Invalid("vout.1 is normal for bet");
451                     else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
452                         return eval->Invalid("always should find vin.0, but didnt for bet");
453                     else if ( vinTx.vout[1].scriptPubKey != fundingPubKey )
454                         return eval->Invalid("entropy tx not fundingPubKey for bet");
455                     else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 )
456                         return eval->Invalid("vout[0] != entropy nValue for bet");
457                     else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 )
458                         return eval->Invalid("vout[1] constrain violation for bet");
459                     else if ( tx.vout[2].nValue >= txfee+maxodds || tx.vout[2].nValue < txfee )
460                         return eval->Invalid("vout[2] nValue violation for bet");
461                     else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 )
462                         return eval->Invalid("always should find vinofvin.0, but didnt for bet");
463                     else if ( vinTx.vin[0].prevout.hash != fundingtxid )
464                     {
465                         if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
466                         {
467                             uint8_t *ptr0,*ptr1; int32_t i; char str[65];
468                             fprintf(stderr,"bidTx.%s\n",uint256_str(str,txid));
469                             fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n);
470                             fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n);
471                             ptr0 = (uint8_t *)vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.data();
472                             ptr1 = (uint8_t *)fundingPubKey.data();
473                             for (i=0; i<vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.size(); i++)
474                                 fprintf(stderr,"%02x",ptr0[i]);
475                             fprintf(stderr," script vs ");
476                             for (i=0; i<fundingPubKey.size(); i++)
477                                 fprintf(stderr,"%02x",ptr1[i]);
478                             fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,vinTx.vin[0].prevout.n,uint256_str(str,vinTx.vin[0].prevout.hash));
479                             return eval->Invalid("vin1 of entropy tx not fundingPubKey for bet");
480                         }
481                     }
482                     if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
483                     {
484                         // will only happen for fundingPubKey
485                         DiceQueue(iswin,sbits,fundingtxid,txid);
486                     }
487                     break;
488                 case 'L':
489                 case 'W':
490                 case 'T':
491                     //vin.0: normal input
492                     //vin.1: betTx CC vout.0 entropy from bet
493                     //vin.2: betTx CC vout.1 bet amount from bet
494                     //vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
495                     //vout.1: tag to owner address for entropy funds
496                     preventCCvouts = 1;
497                     DiceAmounts(inputs,outputs,cp,eval,tx);
498                     if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 )
499                         return eval->Invalid("vin0 or vin1 normal vin for bet");
500                     else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash )
501                         return eval->Invalid("vin0 != vin1 prevout.hash for bet");
502                     else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
503                         return eval->Invalid("always should find vin.0, but didnt for wlt");
504                     else if ( vinTx.vout.size() < 3 || DecodeDiceOpRet(tx.vin[1].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,vinhentropy,vinproof) != 'B' )
505                         return eval->Invalid("not betTx for vin0/1 for wlt");
506                     else if ( sbits != vinsbits || fundingtxid != vinfundingtxid )
507                         return eval->Invalid("sbits or fundingtxid mismatch for wlt");
508                     else if ( fundingPubKey != tx.vout[1].scriptPubKey )
509                         return eval->Invalid("tx.vout[1] != fundingPubKey for wlt");
510                     if ( funcid == 'L' )
511                     {
512                         //vout.0: funding CC to entropy owner
513                         //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
514                         if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 )
515                             return eval->Invalid("vout[0] != inputs-txfee for loss");
516                         else if ( tx.vout[2].scriptPubKey != fundingPubKey )
517                         {
518                             if ( tx.vout[2].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[2].scriptPubKey.data())[0] != 0x6a )
519                                 return eval->Invalid("vout[2] not send to fundingPubKey for loss");
520                         }
521                         iswin = -1;
522                     }
523                     else
524                     {
525                         //vout.0: funding CC change to entropy owner
526                         //vout.2: normal output to bettor's address
527                         //vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
528                         odds = vinTx.vout[2].nValue - txfee;
529                         if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
530                             return eval->Invalid("vout[0] != inputs-txfee for win/timeout");
531                         else if ( tx.vout[2].scriptPubKey != vinTx.vout[2].scriptPubKey )
532                             return eval->Invalid("vout[2] scriptPubKey mismatch for win/timeout");
533                         else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue )
534                             return eval->Invalid("vout[2] payut mismatch for win/timeout");
535                         else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) )
536                         {
537                             fprintf(stderr,"inputs %.8f != outputs %.8f + %.8f\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[2].nValue/COIN);
538                             return eval->Invalid("CC funds mismatch for win/timeout");
539                         }
540                         else if ( tx.vout[3].scriptPubKey != fundingPubKey )
541                         {
542                             if ( tx.vout[3].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[3].scriptPubKey.data())[0] != 0x6a )
543                                 return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout");
544                         }
545                         iswin = (funcid == 'W');
546                     }
547                     if ( iswin != 0 )
548                     {
549                         //char str[65],str2[65];
550                         entropy = DiceGetEntropy(vinTx,'B');
551                         vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32);
552                         //fprintf(stderr,"calculated house hentropy.%s\n",uint256_str(str,hash));
553                         //fprintf(stderr,"verify house entropy %s vs bettor %s\n",uint256_str(str,proof),uint256_str(str2,entropy));
554                         winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy);
555                         if ( (winnings == 0 && iswin > 0) || (winnings > 0 && iswin < 0) )
556                             return eval->Invalid("DiceCalc mismatch for win/loss");
557                     }
558                     else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 )
559                         return eval->Invalid("invalid timeout claim for timeout");
560                     break;
561             }
562         }
563         return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
564     }
565     return(true);
566 }
567
568 uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
569 {
570     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;
571     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
572     GetCCaddress(cp,coinaddr,pk);
573     SetCCunspents(unspentOutputs,coinaddr);
574     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
575     {
576         txid = it->first.txhash;
577         vout = (int32_t)it->first.index;
578         if ( it->second.satoshis < 1000000 )
579             continue;
580         //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
581         for (j=0; j<mtx.vin.size(); j++)
582             if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
583                 break;
584         if ( j != mtx.vin.size() )
585             continue;
586         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
587         {
588             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
589             {
590                 if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
591                 {
592                     if ( total != 0 && maxinputs != 0 )
593                         mtx.vin.push_back(CTxIn(txid,vout,CScript()));
594                     totalinputs += it->second.satoshis;
595                     n++;
596                     if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
597                         break;
598                 }
599             } else fprintf(stderr,"null funcid\n");
600         }
601     }
602     return(totalinputs);
603 }
604
605 uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid)
606 {
607     char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0; uint8_t funcid;
608     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
609     if ( GetTransaction(reffundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 )
610     {
611         fundingPubKey = tx.vout[1].scriptPubKey;
612     } else return(0);
613     GetCCaddress(cp,coinaddr,dicepk);
614     SetCCunspents(unspentOutputs,coinaddr);
615     entropyval = 0;
616     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
617     {
618         txid = it->first.txhash;
619         vout = (int32_t)it->first.index;
620         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
621         {
622             //char str[65],str2[65];
623             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
624             {
625                 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
626                 {
627                     if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T')  )
628                     {
629                         if ( funcid != 'F' && funcid != 'T' )
630                         {
631                             n++;
632                             fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN);
633                         }
634                         totalinputs += nValue;
635                         if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
636                         {
637                             fprintf(stderr,"check first\n");
638                             if ( fundingPubKey == tx.vout[1].scriptPubKey )
639                             {
640                                 if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash )
641                                 {
642                                     if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 )
643                                     {
644                                         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());
645                                         continue;
646                                     }
647                                     if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
648                                     {
649                                         uint8_t *ptr0,*ptr1; int32_t i; char str[65];
650                                         ptr0 = (uint8_t *)vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.data();
651                                         ptr1 = (uint8_t *)fundingPubKey.data();
652                                         for (i=0; i<vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.size(); i++)
653                                             fprintf(stderr,"%02x",ptr0[i]);
654                                         fprintf(stderr," script vs ");
655                                         for (i=0; i<fundingPubKey.size(); i++)
656                                             fprintf(stderr,"%02x",ptr1[i]);
657                                         fprintf(stderr," (%c) entropy vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
658                                         continue;
659                                     }
660                                 } //else fprintf(stderr,"not E or is funding\n");
661                                 entropytxid = txid;
662                                 entropyval = tx.vout[0].nValue;
663                                 first = 1;
664                             }
665                             else
666                             {
667                                 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
668                                 ptr0 = (uint8_t *)tx.vout[1].scriptPubKey.data();
669                                 ptr1 = (uint8_t *)fundingPubKey.data();
670                                 for (i=0; i<tx.vout[1].scriptPubKey.size(); i++)
671                                     fprintf(stderr,"%02x",ptr0[i]);
672                                 fprintf(stderr," script vs ");
673                                 for (i=0; i<fundingPubKey.size(); i++)
674                                     fprintf(stderr,"%02x",ptr1[i]);
675                                 fprintf(stderr," (%c) tx vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
676                             }
677                         }
678                     } 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);
679                 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
680             } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
681         }
682     }
683     fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN);
684     return(totalinputs);
685 }
686
687 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)
688 {
689     char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
690     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
691     GetCCaddress(cp,CCaddr,dicepk);
692     SetCCtxids(txids,cp->normaladdr);
693     if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
694     {
695         if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
696         {
697             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
698             {
699                 fundingPubKey = tx.vout[1].scriptPubKey;
700                 return(true);
701             }
702         }
703         return(false);
704     }
705     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
706     {
707         //int height = it->first.blockHeight;
708         txid = it->first.txhash;
709         if ( fundingtxid != zeroid && txid != fundingtxid )
710             continue;
711         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
712         {
713             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
714             {
715                 if ( sbits == refsbits )
716                 {
717                     fundingPubKey = tx.vout[1].scriptPubKey;
718                     fundingtxid = txid;
719                     return(true);
720                 }
721             }
722         }
723     }
724     return(false);
725 }
726
727 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)
728 {
729     struct CCcontract_info *cp; int32_t cmpflag;
730     cp = CCinit(C,EVAL_DICE);
731     if ( txfee == 0 )
732         txfee = 10000;
733     mypk = pubkey2pk(Mypubkey());
734     dicepk = GetUnspendable(cp,0);
735     sbits = stringbits(planstr);
736     if ( reffundingtxid == zeroid )
737         cmpflag = 0;
738     else cmpflag = 1;
739     if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
740     {
741         fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
742         return(0);
743     }
744     return(cp);
745 }
746
747 UniValue DiceInfo(uint256 diceid)
748 {
749     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;
750     if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
751     {
752         fprintf(stderr,"cant find fundingtxid\n");
753         result.push_back(Pair("error","cant find fundingtxid"));
754         return(result);
755     }
756     if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
757     {
758         fprintf(stderr,"fundingtxid isnt dice creation txid\n");
759         result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
760         return(result);
761     }
762     result.push_back(Pair("result","success"));
763     result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
764     unstringbits(str,sbits);
765     result.push_back(Pair("name",str));
766     result.push_back(Pair("sbits",sbits));
767     sprintf(numstr,"%.8f",(double)minbet/COIN);
768     result.push_back(Pair("minbet",numstr));
769     sprintf(numstr,"%.8f",(double)maxbet/COIN);
770     result.push_back(Pair("maxbet",numstr));
771     result.push_back(Pair("maxodds",maxodds));
772     result.push_back(Pair("timeoutblocks",timeoutblocks));
773     cp = CCinit(&C,EVAL_DICE);
774     dicepk = GetUnspendable(cp,0);
775     funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
776     sprintf(numstr,"%.8f",(double)funding/COIN);
777     result.push_back(Pair("funding",numstr));
778     return(result);
779 }
780
781 UniValue DiceList()
782 {
783     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];
784     cp = CCinit(&C,EVAL_DICE);
785     SetCCtxids(addressIndex,cp->normaladdr);
786     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
787     {
788         txid = it->first.txhash;
789         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
790         {
791             if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
792             {
793                 result.push_back(uint256_str(str,txid));
794             }
795         }
796     }
797     return(result);
798 }
799
800 std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
801 {
802     CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
803     if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
804     {
805         fprintf(stderr,"negative parameter error\n");
806         return(0);
807     }
808     memset(&zero,0,sizeof(zero));
809     if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
810         return(0);
811     if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 )
812     {
813         mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
814         mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
815         mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
816         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
817     }
818     fprintf(stderr,"cant find enough inputs\n");
819     return(0);
820 }
821
822 std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
823 {
824     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;
825     if ( amount < 0 )
826     {
827         fprintf(stderr,"negative parameter error\n");
828         return(0);
829     }
830     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
831         return(0);
832     scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
833     if ( 0 )
834     {
835         uint8_t *ptr0,*ptr1; int32_t i;
836         ptr0 = (uint8_t *)scriptPubKey.data();
837         ptr1 = (uint8_t *)fundingPubKey.data();
838         for (i=0; i<35; i++)
839             fprintf(stderr,"%02x",ptr0[i]);
840         fprintf(stderr," script vs ");
841         for (i=0; i<35; i++)
842             fprintf(stderr,"%02x",ptr1[i]);
843         fprintf(stderr," funding\n");
844     }
845     if ( scriptPubKey == fundingPubKey )
846     {
847         if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 )
848         {
849             hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
850             mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
851             mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
852             return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
853         } else fprintf(stderr,"cant find enough inputs\n");
854     } else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
855     return(0);
856 }
857
858 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
859 {
860     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;
861     if ( bet < 0 || odds < 1 )
862     {
863         fprintf(stderr,"negative parameter error\n");
864         return(0);
865     }
866     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
867         return(0);
868     if ( bet < minbet || bet > maxbet || odds > maxodds )
869     {
870         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);
871         return(0);
872     }
873     if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
874     {
875         if ( myIsutxo_spentinmempool(entropytxid,0) != 0 )
876         {
877             fprintf(stderr,"entropy txid is spent\n");
878             return(0);
879         }
880         mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
881         if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
882         {
883             hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
884             mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
885             mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
886             mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
887             return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
888         } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
889     }
890     if ( entropyval == 0 && funding != 0 )
891         fprintf(stderr,"cant find dice entropy inputs\n");
892     else fprintf(stderr,"cant find dice inputs\n");
893     return(0);
894 }
895
896 std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
897 {
898     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,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid; int32_t iswin=0; uint64_t entropyval,sbits;
899     *resultp = 0;
900     //char str[65]; fprintf(stderr,"DiceBetFinish.%s %s\n",planstr,uint256_str(str,bettxid));
901     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
902     {
903         fprintf(stderr,"Diceinit error\n");
904         return("0");
905     }
906     fundingpk = DiceFundingPk(fundingPubKey);
907     if ( winlosetimeout != 0 )
908     {
909         scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
910         if ( scriptPubKey != fundingPubKey )
911         {
912             fprintf(stderr,"only dice fund creator can submit winner or loser\n");
913             return("0");
914         }
915     }
916     if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 )
917     {
918         fprintf(stderr,"no txfee inputs for win/lose\n");
919         return("0");
920     }
921     if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
922     {
923         bettorentropy = DiceGetEntropy(betTx,'B');
924         if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
925         {
926             winlosetimeout = iswin;
927             if ( iswin == winlosetimeout )
928             {
929                 if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 )
930                 {
931                     fprintf(stderr,"bettxid already spent\n");
932                     return("0");
933                 }
934                 //fprintf(stderr,"iswin.%d matches\n",iswin);
935                 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
936                 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
937                 if ( iswin == 0 )
938                 {
939                     funcid = 'T';
940                     if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
941                     {
942                         fprintf(stderr,"timeout is not supported yet\n");
943                         return("0");
944                     }
945                     else
946                     {
947                         hentropy = hentropyproof = zeroid;
948                         iswin = 1;
949                     }
950                 }
951                 if ( iswin > 0 )
952                 {
953                     funcid = 'W';
954                     odds = (betTx.vout[2].nValue - txfee);
955                     if ( odds < 1 || odds > maxodds )
956                     {
957                         fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
958                         return("0");
959                     }
960                     CCchange = betTx.vout[0].nValue;
961                     fundsneeded = txfee + odds*betTx.vout[1].nValue;
962                     if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60)) > 0 )
963                     {
964                         if ( inputs > fundsneeded )
965                             CCchange += (inputs - fundsneeded);
966                         mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
967                         mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
968                         mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
969                     }
970                     else
971                     {
972                         fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
973                         return("0");
974                     }
975                 }
976                 else
977                 {
978                     funcid = 'L';
979                     mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk));
980                     mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
981                 }
982                 if ( winlosetimeout != 0 )
983                     hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
984                 *resultp = 1;
985                 //char str[65],str2[65];
986                 //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy));
987                 return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
988             } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
989         }
990         else
991         {
992             *resultp = -1;
993             fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
994             return("0");
995         }
996     }
997     *resultp = -1;
998     fprintf(stderr,"couldnt find bettx or entropytx\n");
999     return("0");
1000 }
1001
1002 double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid)
1003 {
1004     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;
1005     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1006     {
1007         fprintf(stderr,"Diceinit error\n");
1008         return(0.);
1009     }
1010     fundingpk = DiceFundingPk(fundingPubKey);
1011     scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
1012     GetCCaddress(cp,coinaddr,dicepk);
1013     if ( bettxid == zeroid ) // scan
1014     {
1015         std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1016         SetCCunspents(unspentOutputs,coinaddr);
1017         for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
1018         {
1019             txid = it->first.txhash;
1020             vout = (int32_t)it->first.index;
1021             if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
1022             {
1023                 if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' )
1024                 {
1025                     res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey);
1026                     if ( result > 0 )
1027                     {
1028                         mySendrawtransaction(res);
1029                         n++;
1030                     }
1031                 }
1032             }
1033         }
1034         if ( scriptPubKey == fundingPubKey )
1035         {
1036             for (i=0; i<=n; i++)
1037             {
1038                 res = DiceAddfunding(txfee,planstr,fundingtxid,COIN);
1039                 mySendrawtransaction(res);
1040             }
1041         }
1042         return(n);
1043     }
1044     else
1045     {
1046         if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1047         {
1048             if ( GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() > 2 )
1049             {
1050                 if ( spenttx.vout[2].scriptPubKey == fundingPubKey )
1051                     return(0.);
1052                 else return((double)spenttx.vout[2].nValue/COIN);
1053             } else return(0.);
1054         }
1055         else if ( scriptPubKey == fundingPubKey )
1056             res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,1);
1057         else res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,0);
1058         if ( result > 0 )
1059         {
1060             mySendrawtransaction(res);
1061             sleep(1);
1062             if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1063             {
1064                 if ( GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() >= 2 )
1065                 {
1066                     if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a )
1067                         return(0.);
1068                     else return((double)spenttx.vout[2].nValue/COIN);
1069                 } else return(0.);
1070             }
1071             fprintf(stderr,"didnt find dicefinish tx\n");
1072         } else return(-1.);
1073     }
1074     return(0.);
1075 }
This page took 0.083371 seconds and 4 git commands to generate.