]> Git Repo - VerusCoin.git/blob - src/cc/dice.cpp
Bet
[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, validate
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
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 winner:
65  vin.0: betTx CC vout.0 entropy from bet
66  vin.1: betTx CC vout.1 bet amount from bet
67  vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
68  vout.0: funding CC change to entropy owner
69  vout.1: tag to owner address for entropy funds
70  vout.2: normal output to bettor's address
71  vout.n-1: opreturn 'W' sbits fundingtxid hentropy
72
73 loser:
74  vin.0: betTx CC vout.0 entropy from bet
75  vin.1: betTx CC vout.1 bet amount from bet
76  vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
77  vout.0: funding CC to entropy owner
78  vout.1: tag to owner address for entropy funds
79  vout.n-1: opreturn 'L' sbits fundingtxid hentropy
80
81 timeout:
82  vin.0: betTx CC vout.0 entropy from bet
83  vin.1: betTx CC vout.1 bet amount from bet
84  vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
85  vout.0: funding CC vin.0 to entropy owner
86  vout.1: tag to owner address for entropy funds
87  vout.2: normal vin.1 to bettor's address
88  vout.n-1: opreturn 'T' sbits fundingtxid hentropy
89
90  
91  */
92
93 #include "../endian.h"
94
95 static uint256 bettxids[128];
96
97 struct dicefinish_info
98 {
99     uint256 fundingtxid,bettxid;
100     uint64_t sbits;
101     int32_t iswin;
102 };
103
104 void *dicefinish(void *_ptr)
105 {
106     char str[65],str2[65],name[32]; std::string res; int32_t i,duplicate=0; struct dicefinish_info *ptr;
107     ptr = (struct dicefinish_info *)_ptr;
108     sleep(1);
109     for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
110         if ( bettxids[i] == ptr->bettxid )
111         {
112             duplicate = 1;
113             break;
114         }
115     if ( duplicate == 0 )
116     {
117         for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
118             if ( bettxids[i] == zeroid )
119             {
120                 bettxids[i] = ptr->bettxid;
121                 break;
122             }
123         if ( i == sizeof(bettxids)/sizeof(*bettxids) )
124             bettxids[rand() % i] = ptr->bettxid;
125     }
126     unstringbits(name,ptr->sbits);
127     fprintf(stderr,"duplicate.%d dicefinish.%d %s funding.%s bet.%s\n",duplicate,ptr->iswin,name,uint256_str(str,ptr->fundingtxid),uint256_str(str2,ptr->bettxid));
128     if ( duplicate == 0 )
129     {
130         CTransaction tx; uint256 txid; char str[65]; int32_t result;
131         res = DiceWinLoseTimeout(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
132         if ( result != 0 && res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
133         {
134             LOCK(cs_main);
135             if ( DecodeHexTx(tx,res) != 0 )
136             {
137                 txid = tx.GetHash();
138                 RelayTransaction(tx);
139                 fprintf(stderr,"%s\nresult.(%s)\n",res.c_str(),uint256_str(str,txid));
140             }
141         }
142     }
143     free(ptr);
144     return(0);
145 }
146
147 void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
148 {
149     struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
150     ptr->fundingtxid = fundingtxid;
151     ptr->bettxid = bettxid;
152     ptr->sbits = sbits;
153     ptr->iswin = iswin;
154     if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
155     {
156         fprintf(stderr,"DiceQueue.%d\n",iswin);
157     } // small memory leak per DiceQueue
158 }
159
160 void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
161 {
162     int32_t i,j=0;
163 #if defined(WORDS_BIGENDIAN)
164     for (i=31; i>=0; i--)
165         dest[j++] = src[i];
166 #else
167     memcpy(dest,src,len);
168 #endif
169 }
170
171 uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
172 {
173     int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
174     memset(&hentropy,0,32);
175     endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
176     txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
177     txidpub = curve25519(txidpriv,curve25519_basepoint9());
178
179     Myprivkey(tmp256.bytes);
180     vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
181     mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
182     mypub = curve25519(mypriv,curve25519_basepoint9());
183
184     ssecret = curve25519(mypriv,txidpub);
185     ssecret2 = curve25519(txidpriv,mypub);
186     if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
187     {
188         vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
189         vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
190         endiancpy((uint8_t *)&entropy,_entropy,32);
191         endiancpy((uint8_t *)&hentropy,_hentropy,32);
192     }
193     else
194     {
195         for (i=0; i<32; i++)
196             fprintf(stderr,"%02x",ssecret.bytes[i]);
197         fprintf(stderr," ssecret\n");
198         for (i=0; i<32; i++)
199             fprintf(stderr,"%02x",ssecret2.bytes[i]);
200         fprintf(stderr," ssecret2 dont match\n");
201     }
202     return(hentropy);
203 }
204
205 uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
206 {
207     uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
208     if ( odds < 10000 )
209         return(0);
210     else odds -= 10000;
211     if ( bet < minbet || bet > maxbet || odds > maxodds )
212     {
213         fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
214         return(0);
215     }
216     endiancpy(buf,(uint8_t *)&houseentropy,32);
217     endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
218     vcalc_sha256(0,(uint8_t *)&_house,buf,64);
219     endiancpy((uint8_t *)&house,_house,32);
220
221     endiancpy(buf,(uint8_t *)&bettorentropy,32);
222     endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
223     vcalc_sha256(0,(uint8_t *)&_house,buf,64);
224     endiancpy((uint8_t *)&bettor,_bettor,32);
225     if ( odds > 1 )
226         bettor = (bettor / arith_uint256(odds));
227     if ( bettor >= house )
228         winnings = bet * odds;
229     else winnings = 0;
230     fprintf(stderr,"winnings %.8f bet %.8f at odds %d:1 %s vs %s\n",(double)winnings/COIN,(double)bet/COIN,(int32_t)odds,uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
231     return(winnings);
232 }
233
234 CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
235 {
236     CScript opret; uint8_t evalcode = EVAL_DICE;
237     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
238     return(opret);
239 }
240
241 uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
242 {
243     std::vector<uint8_t> vopret; uint8_t *script,e,f;
244     GetOpReturnData(scriptPubKey, vopret);
245     script = (uint8_t *)vopret.data();
246     if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
247     {
248         if ( e == EVAL_DICE && f == 'F' )
249             return(f);
250     }
251     return(0);
252 }
253
254 CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
255 {
256     CScript opret; uint8_t evalcode = EVAL_DICE;
257     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
258     return(opret);
259 }
260
261 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
262 {
263     std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
264     GetOpReturnData(scriptPubKey,vopret);
265     if ( vopret.size() > 2 )
266     {
267         script = (uint8_t *)vopret.data();
268         if ( script[0] == EVAL_DICE )
269         {
270             if ( script[1] == 'F' )
271             {
272                 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
273                 {
274                     memset(&hash,0,32);
275                     fundingtxid = txid;
276                     return('F');
277                 } else fprintf(stderr,"unmarshal error for F\n");
278             }
279             else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
280             {
281                 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
282                     return(f);
283                 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
284             }
285         } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
286     } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
287     return(0);
288 }
289
290 uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
291 {
292     uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
293     if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
294         return(hash);
295     else return(zeroid);
296 }
297
298 uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
299 {
300     char destaddr[64];
301     if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
302     {
303         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
304             return(tx.vout[v].nValue);
305     }
306     return(0);
307 }
308
309 bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
310 {
311     static uint256 zerohash;
312     CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
313     numvins = tx.vin.size();
314     numvouts = tx.vout.size();
315     for (i=0; i<numvins; i++)
316     {
317         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
318         {
319             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
320                 return eval->Invalid("always should find vin, but didnt");
321             else
322             {
323                 if ( hashBlock == zerohash )
324                     return eval->Invalid("cant dice from mempool");
325                 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
326                     inputs += assetoshis;
327             }
328         }
329     }
330     for (i=0; i<numvouts; i++)
331     {
332         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
333         if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
334             outputs += assetoshis;
335     }
336     if ( inputs != outputs+txfee )
337     {
338         fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
339         return eval->Invalid("mismatched inputs != outputs + txfee");
340     }
341     else return(true);
342 }
343
344 bool DiceIsmine(const CScript scriptPubKey)
345 {
346     char destaddr[64],myaddr[64];
347     Getscriptaddress(destaddr,scriptPubKey);
348     Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
349     return(strcmp(destaddr,myaddr) == 0);
350 }
351
352 int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction vinTx,uint256 bettorentropy,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 fundingtxid)
353 {
354     uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
355     //char str[65],str2[65];
356     if ( DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout.size() > 0 )
357     {
358         if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') && sbits == vinsbits && fundingtxid == vinfundingtxid )
359         {
360             hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
361             if ( hentropy == hentropy2 )
362             {
363                 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
364                 //fprintf(stderr,"I am house entropy %.8f entropy.(%s) vs %s -> winnings %.8f\n",(double)vinTx.vout[0].nValue/COIN,uint256_str(str,entropy),uint256_str(str2,hash),(double)winnings/COIN);
365                 if ( winnings == 0 )
366                 {
367                     // queue 'L' losing tx
368                     return(-1);
369                 }
370                 else
371                 {
372                     // queue 'W' winning tx
373                     return(1);
374                 }
375             }
376         }
377     }
378     return(0);
379 }
380
381 bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
382 {
383     uint256 txid,fundingtxid,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
384     numvins = tx.vin.size();
385     numvouts = tx.vout.size();
386     preventCCvins = preventCCvouts = -1;
387     if ( numvouts < 1 )
388         return eval->Invalid("no vouts");
389     else
390     {
391         txid = tx.GetHash();
392         if ( (funcid=  DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
393         {
394             if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
395                 return eval->Invalid("cant find fundingtxid");
396             else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
397                 return eval->Invalid("fundingTx not valid");
398             switch ( funcid )
399             {
400                 case 'F':
401                     //vins.*: normal inputs
402                     //vout.0: CC vout for funding
403                     //vout.1: normal marker vout for easy searching
404                     //vout.2: normal change
405                     //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
406                     return eval->Invalid("unexpected DiceValidate for createfunding");
407                     break;
408                 case 'E': // check sig of vin to match fundingtxid
409                     //vins.*: normal inputs
410                     //vout.0: CC vout for locked entropy funds
411                     //vout.1: tag to owner address for entropy funds
412                     //vout.2: normal change
413                     //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
414                     return eval->Invalid("unexpected DiceValidate for addfunding entropy");
415                     break;
416                 case 'B':
417                     //vin.0: entropy txid from house
418                     //vins.1+: normal inputs
419                     //vout.0: CC vout for locked entropy
420                     //vout.1: CC vout for locked bet
421                     //vout.2: tag for bettor's address (txfee + odds)
422                     //vout.3: change
423                     //vout.n-1: opreturn 'B' sbits fundingtxid entropy
424                     if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
425                         return eval->Invalid("always should find vin.0, but didnt");
426                     if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
427                     {
428                         fprintf(stderr,"DiceIsWinner.%d\n",iswin);
429                         DiceQueue(iswin,sbits,fundingtxid,txid);
430                     }
431                     //return eval->Invalid("dont confirm bet during debug");
432                     break;
433                 case 'L':
434                     //vin.0: betTx CC vout.0 entropy from bet
435                     //vin.1: betTx CC vout.1 bet amount from bet
436                     //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
437                     //vout.0: funding CC to entropy owner
438                     //vout.1: tag to owner address for entropy funds
439                     //vout.n-1: opreturn 'L' sbits fundingtxid hentropy
440                     break;
441                 case 'W':
442                     //vin.0: betTx CC vout.0 entropy from bet
443                     //vin.1: betTx CC vout.1 bet amount from bet
444                     //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
445                     //vout.0: funding CC change to entropy owner
446                     //vout.1: tag to owner address for entropy funds
447                     //vout.2: normal output to bettor's address
448                     //vout.n-1: opreturn 'W' sbits fundingtxid hentropy
449                     break;
450                 case 'T':
451                     //vin.0: betTx CC vout.0 entropy from bet
452                     //vin.1: betTx CC vout.1 bet amount from bet
453                     //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
454                     //vout.0: funding CC vin.0 to entropy owner
455                     //vout.1: tag to owner address for entropy funds
456                     //vout.2: normal vin.1 to bettor's address
457                     //vout.n-1: opreturn 'T' sbits fundingtxid hentropy
458                     /*for (i=0; i<numvins; i++)
459                     {
460                         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
461                             return eval->Invalid("unexpected normal vin for unlock");
462                     }
463                     if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
464                         return false;
465                     else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
466                         return eval->Invalid("always should find vin.0, but didnt");
467                     else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
468                         return eval->Invalid("lock tx vout.0 is normal output");
469                     else if ( tx.vout.size() < 3 )
470                         return eval->Invalid("unlock tx not enough vouts");
471                     else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
472                         return eval->Invalid("unlock tx vout.0 is normal output");
473                     else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
474                         return eval->Invalid("unlock tx vout.1 is CC output");
475                     else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
476                         return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
477                     amount = vinTx.vout[0].nValue;
478                     reward = 0;
479                     //reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,timeoutblocks);
480                     if ( tx.vout[1].nValue > amount+reward )
481                         return eval->Invalid("unlock tx vout.1 isnt amount+reward");
482                     preventCCvouts = 1;*/
483                     break;
484             }
485         }
486         return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
487     }
488     return(true);
489 }
490
491 uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
492 {
493     char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid;
494     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
495     GetCCaddress(cp,coinaddr,pk);
496     SetCCunspents(unspentOutputs,coinaddr);
497     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
498     {
499         txid = it->first.txhash;
500         vout = (int32_t)it->first.index;
501         if ( it->second.satoshis < 1000000 )
502             continue;
503         fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
504         for (j=0; j<mtx.vin.size(); j++)
505             if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
506                 break;
507         if ( j != mtx.vin.size() )
508             continue;
509         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
510         {
511             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
512             {
513                 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
514                 if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'W' && funcid != 'L' && funcid != 'T' )
515                     continue;
516                 else if ( fundsflag == 0 && funcid != 'B' )
517                     continue;
518                 if ( total != 0 && maxinputs != 0 )
519                 {
520                     if ( fundsflag == 0 )
521                         scriptPubKey = tx.vout[1].scriptPubKey;
522                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
523                 }
524                 totalinputs += it->second.satoshis;
525                 n++;
526                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
527                     break;
528             } else fprintf(stderr,"null funcid\n");
529         }
530     }
531     return(totalinputs);
532 }
533
534 uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
535 {
536     char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CTransaction tx; int32_t vout,first=0; uint8_t funcid;
537     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
538     GetCCaddress(cp,coinaddr,pk);
539     SetCCunspents(unspentOutputs,coinaddr);
540     entropyval = 0;
541     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
542     {
543         txid = it->first.txhash;
544         vout = (int32_t)it->first.index;
545         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
546         {
547             char str[65],str2[65];
548             if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
549             {
550                 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
551                 {
552                     if ( funcid != 'B' && refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 )
553                     {
554                         totalinputs += nValue;
555                         fprintf(stderr,"add %.8f\n",(double)nValue/COIN);
556                         if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
557                         {
558                             entropytxid = txid;
559                             entropyval = tx.vout[0].nValue;
560                             first = 1;
561                         }
562                     } //else fprintf(stderr,"%c refsbits.%llx sbits.%llx nValue %.8f\n",funcid,(long long)refsbits,(long long)sbits,(double)nValue/COIN);
563                 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
564             } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
565         }
566     }
567     return(totalinputs);
568 }
569
570 bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
571 {
572     char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
573     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
574     GetCCaddress(cp,CCaddr,dicepk);
575     SetCCtxids(txids,cp->normaladdr);
576     if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
577     {
578         if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
579         {
580             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
581             {
582                 fundingPubKey = tx.vout[1].scriptPubKey;
583                 return(true);
584             }
585         }
586         return(false);
587     }
588     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
589     {
590         //int height = it->first.blockHeight;
591         txid = it->first.txhash;
592         if ( fundingtxid != zeroid && txid != fundingtxid )
593             continue;
594         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
595         {
596             if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
597             {
598                 if ( sbits == refsbits )
599                 {
600                     fundingPubKey = tx.vout[1].scriptPubKey;
601                     fundingtxid = txid;
602                     return(true);
603                 }
604             }
605         }
606     }
607     return(false);
608 }
609
610 struct CCcontract_info *Diceinit(CScript &fundingPubKey,uint256 reffundingtxid,struct CCcontract_info *C,char *planstr,uint64_t &txfee,CPubKey &mypk,CPubKey &dicepk,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
611 {
612     struct CCcontract_info *cp; int32_t cmpflag;
613     cp = CCinit(C,EVAL_DICE);
614     if ( txfee == 0 )
615         txfee = 10000;
616     mypk = pubkey2pk(Mypubkey());
617     dicepk = GetUnspendable(cp,0);
618     sbits = stringbits(planstr);
619     if ( reffundingtxid == zeroid )
620         cmpflag = 0;
621     else cmpflag = 1;
622     if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
623     {
624         fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
625         return(0);
626     }
627     return(cp);
628 }
629
630 UniValue DiceInfo(uint256 diceid)
631 {
632     UniValue result(UniValue::VOBJ); CPubKey dicepk; uint256 hashBlock,entropytxid; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,funding,entropyval; char str[67],numstr[65]; struct CCcontract_info *cp,C;
633     if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
634     {
635         fprintf(stderr,"cant find fundingtxid\n");
636         result.push_back(Pair("error","cant find fundingtxid"));
637         return(result);
638     }
639     if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
640     {
641         fprintf(stderr,"fundingtxid isnt dice creation txid\n");
642         result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
643         return(result);
644     }
645     result.push_back(Pair("result","success"));
646     result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
647     unstringbits(str,sbits);
648     result.push_back(Pair("name",str));
649     result.push_back(Pair("sbits",sbits));
650     sprintf(numstr,"%.8f",(double)minbet/COIN);
651     result.push_back(Pair("minbet",numstr));
652     sprintf(numstr,"%.8f",(double)maxbet/COIN);
653     result.push_back(Pair("maxbet",numstr));
654     result.push_back(Pair("maxodds",maxodds));
655     result.push_back(Pair("timeoutblocks",timeoutblocks));
656     cp = CCinit(&C,EVAL_DICE);
657     dicepk = GetUnspendable(cp,0);
658     funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
659     sprintf(numstr,"%.8f",(double)funding/COIN);
660     result.push_back(Pair("funding",numstr));
661     return(result);
662 }
663
664 UniValue DiceList()
665 {
666     UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65];
667     cp = CCinit(&C,EVAL_DICE);
668     SetCCtxids(addressIndex,cp->normaladdr);
669     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
670     {
671         txid = it->first.txhash;
672         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
673         {
674             if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
675             {
676                 result.push_back(uint256_str(str,txid));
677             }
678         }
679     }
680     return(result);
681 }
682
683 std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
684 {
685     CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
686     if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
687     {
688         fprintf(stderr,"negative parameter error\n");
689         return(0);
690     }
691     memset(&zero,0,sizeof(zero));
692     if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
693         return(0);
694     if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
695     {
696         mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
697         mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
698         mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
699         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
700     }
701     fprintf(stderr,"cant find enough inputs\n");
702     return(0);
703 }
704
705 std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
706 {
707     CMutableTransaction mtx; CScript fundingPubKey,scriptPubKey; uint256 entropy,hentropy; CPubKey mypk,dicepk; uint64_t sbits; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds,timeoutblocks;
708     if ( amount < 0 )
709     {
710         fprintf(stderr,"negative parameter error\n");
711         return(0);
712     }
713     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
714         return(0);
715     scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
716     if ( 0 )
717     {
718         uint8_t *ptr0,*ptr1; int32_t i;
719         ptr0 = (uint8_t *)scriptPubKey.data();
720         ptr1 = (uint8_t *)fundingPubKey.data();
721         for (i=0; i<35; i++)
722             fprintf(stderr,"%02x",ptr0[i]);
723         fprintf(stderr," script vs ");
724         for (i=0; i<35; i++)
725             fprintf(stderr,"%02x",ptr1[i]);
726         fprintf(stderr," funding\n");
727     }
728     if ( scriptPubKey == fundingPubKey )
729     {
730         if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 )
731         {
732             hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
733             mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
734             mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
735             return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
736         } else fprintf(stderr,"cant find enough inputs\n");
737     } else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
738     return(0);
739 }
740
741 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
742 {
743     CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropy,hentropy; struct CCcontract_info *cp,C;
744     if ( bet < 0 || odds < 1 )
745     {
746         fprintf(stderr,"negative parameter error\n");
747         return(0);
748     }
749     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
750         return(0);
751     if ( bet < minbet || bet > maxbet || odds > maxodds )
752     {
753         fprintf(stderr,"Dice plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds);
754         return(0);
755     }
756     if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
757     {
758         mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
759         if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
760         {
761             hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
762             mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
763             mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
764             mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
765             return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
766         } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
767     }
768     if ( entropyval == 0 && funding != 0 )
769         fprintf(stderr,"cant find dice entropy inputs\n");
770     else fprintf(stderr,"cant find dice inputs\n");
771     return(0);
772 }
773
774 std::string DiceWinLoseTimeout(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
775 {
776     CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CPubKey mypk,dicepk; struct CCcontract_info *cp,C; int64_t inputs,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid; int32_t iswin; uint64_t entropyval,sbits;
777     *resultp = 0;
778     if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
779         return("0");
780     if ( winlosetimeout != 0 )
781     {
782         scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
783         if ( scriptPubKey != fundingPubKey )
784         {
785             fprintf(stderr,"only dice fund creator can submit winner or loser\n");
786             return("0");
787         }
788     }
789     if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
790     {
791         bettorentropy = DiceGetEntropy(betTx,'B');
792         if ( (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
793         {
794             if ( iswin == winlosetimeout )
795             {
796                 fprintf(stderr,"iswin.%d matches\n",iswin);
797                 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
798                 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
799                 if ( iswin == 0 )
800                 {
801                     funcid = 'T';
802                     fprintf(stderr,"timeout refunds are not supported yet\n");
803                 }
804                 else if ( iswin > 0 )
805                 {
806                     funcid = 'W';
807                     odds = (betTx.vout[2].nValue - txfee);
808                     if ( odds < 1 || odds > maxodds )
809                     {
810                         fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
811                         return("0");
812                     }
813                     CCchange = betTx.vout[0].nValue;
814                     fundsneeded = 2*txfee + (odds-1)*betTx.vout[1].nValue;
815                     if ( (inputs= AddDiceInputs(scriptPubKey,1,cp,mtx,dicepk,fundsneeded,60)) > 0 )
816                     {
817                         if ( inputs > fundsneeded+txfee )
818                             CCchange += (inputs - (fundsneeded+txfee));
819                         mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
820                         mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
821                         mtx.vout.push_back(CTxOut(odds * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
822                     }
823                     else
824                     {
825                         fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
826                         return("0");
827                     }
828                 }
829                 else
830                 {
831                     funcid = 'L';
832                     mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue - txfee,dicepk));
833                     mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
834                 }
835                 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
836                 *resultp = 1;
837                 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
838             } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
839         } else return("0");
840     }
841     return("0");
842 }
843
This page took 0.073979 seconds and 4 git commands to generate.