]> Git Repo - VerusCoin.git/blame - src/cc/dice.cpp
Test
[VerusCoin.git] / src / cc / dice.cpp
CommitLineData
cfea7a46 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"
cfea7a46 17
2ac06413 18// timeout
afef48c0 19
cfea7a46 20/*
10078e85 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
9025093e 23 1. CC vout locks "house" funds with hash(entropy)
10078e85 24 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout.
9025093e 25 3. house account sends funds to winner/loser with proof of entropy
26 4. if timeout, bettor gets refund
10078e85 27
28 2. and 3. can be done in mempool
9025093e 29
30 The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction.
31
753a6f92 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
7eb60411 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
40createfunding:
41 vins.*: normal inputs
42 vout.0: CC vout for funding
32e3be87 43 vout.1: owner vout
44 vout.2: dice marker address vout for easy searching
45 vout.3: normal change
7eb60411 46 vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
47
48addfunding (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
55bet:
4d2b7323 56 vin.0: entropy txid from house (must validate vin0 of 'E')
7eb60411 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
7eb60411 64loser:
c066bd7b 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'
7eb60411 69 vout.0: funding CC to entropy owner
70 vout.1: tag to owner address for entropy funds
00005381 71 vout.2: change to fundingpk
4d2b7323 72 vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
73
74winner:
75 same as loser, but vout.2 is winnings
00005381 76 vout.3: change to fundingpk
4d2b7323 77 vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
7eb60411 78
02409974 79timeout:
4d2b7323 80 same as winner, just without hentropy or proof
7eb60411 81
10078e85 82 */
cfea7a46 83
ae748eaf 84#include "../endian.h"
85
e787eaee 86static uint256 bettxids[128];
7377f6ea 87
88struct dicefinish_info
89{
e787eaee 90 uint256 fundingtxid,bettxid;
7377f6ea 91 uint64_t sbits;
92 int32_t iswin;
93};
94
b3c09a21 95bool mySendrawtransaction(std::string res)
7e988759 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
7377f6ea 115void *dicefinish(void *_ptr)
116{
7e988759 117 char str[65],str2[65],name[32]; std::string res; int32_t i,result,duplicate=0; struct dicefinish_info *ptr;
7377f6ea 118 ptr = (struct dicefinish_info *)_ptr;
f3ad9d48 119 sleep(3); // wait for bettxid to be in mempool
e787eaee 120 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
121 if ( bettxids[i] == ptr->bettxid )
7377f6ea 122 {
123 duplicate = 1;
124 break;
125 }
126 if ( duplicate == 0 )
127 {
e787eaee 128 for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
129 if ( bettxids[i] == zeroid )
7377f6ea 130 {
e787eaee 131 bettxids[i] = ptr->bettxid;
7377f6ea 132 break;
133 }
e787eaee 134 if ( i == sizeof(bettxids)/sizeof(*bettxids) )
135 bettxids[rand() % i] = ptr->bettxid;
7377f6ea 136 }
137 unstringbits(name,ptr->sbits);
7e988759 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));
3528fb20 139 if ( duplicate == 0 )
140 {
f5d3217a 141 res = DiceBetFinish(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
d74d791a 142 if ( result > 0 )
7e988759 143 mySendrawtransaction(res);
3528fb20 144 }
7377f6ea 145 free(ptr);
146 return(0);
147}
148
e787eaee 149void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
7377f6ea 150{
e8f9de07 151 struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
7377f6ea 152 ptr->fundingtxid = fundingtxid;
e787eaee 153 ptr->bettxid = bettxid;
7377f6ea 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 {
ef979a50 158 //fprintf(stderr,"DiceQueue.%d\n",iswin);
7377f6ea 159 } // small memory leak per DiceQueue
7377f6ea 160}
161
ae748eaf 162void 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
17bc6201 169 memcpy(dest,src,len);
ae748eaf 170#endif
171}
172
00005381 173CPubKey DiceFundingPk(CScript scriptPubKey)
174{
7aee2b87 175 CPubKey pk; uint8_t *ptr,*dest; int32_t i;
00005381 176 if ( scriptPubKey.size() == 35 )
177 {
7aee2b87 178 ptr = (uint8_t *)scriptPubKey.data();
179 dest = (uint8_t *)pk.begin();
00005381 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
dfe4c146 186uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
8138fad2 187{
ae748eaf 188 int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
72362bbe 189 memset(&hentropy,0,32);
e0e655fe 190 endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
d176d2df 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
91f21771 199 ssecret = curve25519(mypriv,txidpub);
200 ssecret2 = curve25519(txidpriv,mypub);
d176d2df 201 if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
8138fad2 202 {
ae748eaf 203 vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
204 vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
dfe4c146 205 endiancpy((uint8_t *)&entropy,_entropy,32);
206 endiancpy((uint8_t *)&hentropy,_hentropy,32);
840a96ca 207 }
208 else
209 {
210 for (i=0; i<32; i++)
774b8b44 211 fprintf(stderr,"%02x",ssecret.bytes[i]);
840a96ca 212 fprintf(stderr," ssecret\n");
213 for (i=0; i<32; i++)
ac9816bb 214 fprintf(stderr,"%02x",ssecret2.bytes[i]);
840a96ca 215 fprintf(stderr," ssecret2 dont match\n");
216 }
ef979a50 217 //char str[65],str2[65];
218 //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy));
8138fad2 219 return(hentropy);
220}
221
9025093e 222uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
10078e85 223{
b52f7685 224 uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
98c6e2e1 225 if ( odds < 10000 )
10078e85 226 return(0);
98c6e2e1 227 else odds -= 10000;
be7a410a 228 if ( bet < minbet || bet > maxbet || odds > maxodds )
229 {
230 fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
231 return(0);
232 }
ef979a50 233 //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,houseentropy),uint256_str(str2,bettorentropy));
aa760cb8 234
be7a410a 235 endiancpy(buf,(uint8_t *)&houseentropy,32);
236 endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
237 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
208f0e7a 238 endiancpy((uint8_t *)&house,_house,32);
be7a410a 239
240 endiancpy(buf,(uint8_t *)&bettorentropy,32);
241 endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
36dcc2e6 242 vcalc_sha256(0,(uint8_t *)&_bettor,buf,64);
208f0e7a 243 endiancpy((uint8_t *)&bettor,_bettor,32);
b52f7685 244 if ( odds > 1 )
245 bettor = (bettor / arith_uint256(odds));
246 if ( bettor >= house )
f3ad9d48 247 winnings = bet * (odds+1);
b52f7685 248 else winnings = 0;
d4c504f0 249 return(winnings);
10078e85 250}
251
9025093e 252CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
10078e85 253{
254 CScript opret; uint8_t evalcode = EVAL_DICE;
9025093e 255 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
10078e85 256 return(opret);
257}
258
9025093e 259uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
10078e85 260{
261 std::vector<uint8_t> vopret; uint8_t *script,e,f;
262 GetOpReturnData(scriptPubKey, vopret);
263 script = (uint8_t *)vopret.data();
9025093e 264 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 265 {
266 if ( e == EVAL_DICE && f == 'F' )
267 return(f);
268 }
269 return(0);
270}
271
afef48c0 272CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
10078e85 273{
274 CScript opret; uint8_t evalcode = EVAL_DICE;
afef48c0 275 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
10078e85 276 return(opret);
277}
278
54519f69 279uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
10078e85 280{
9025093e 281 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
753a6f92 282 GetOpReturnData(scriptPubKey,vopret);
10078e85 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 {
9025093e 290 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 291 {
72362bbe 292 memset(&hash,0,32);
10078e85 293 fundingtxid = txid;
294 return('F');
295 } else fprintf(stderr,"unmarshal error for F\n");
296 }
54519f69 297 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
10078e85 298 {
02409974 299 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
10078e85 300 return(f);
54519f69 301 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
10078e85 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}
cfea7a46 307
a03bb7fa 308uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
309{
54519f69 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 )
a03bb7fa 312 return(hash);
313 else return(zeroid);
314}
315
cfea7a46 316uint64_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
4d2b7323 327int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
cfea7a46 328{
4d2b7323 329 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis;
cfea7a46 330 numvins = tx.vin.size();
331 numvouts = tx.vout.size();
60301f5e 332 inputs = outputs = 0;
cfea7a46 333 for (i=0; i<numvins; i++)
334 {
cfea7a46 335 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
336 {
cfea7a46 337 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
10078e85 338 return eval->Invalid("always should find vin, but didnt");
cfea7a46 339 else
340 {
cfea7a46 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 }
4d2b7323 352 return(inputs - outputs);
cfea7a46 353}
354
1c87477b 355bool DiceIsmine(const CScript scriptPubKey)
356{
357 char destaddr[64],myaddr[64];
358 Getscriptaddress(destaddr,scriptPubKey);
ca05cbaa 359 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
1c87477b 360 return(strcmp(destaddr,myaddr) == 0);
361}
362
aec296a5 363int32_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)
753a6f92 364{
aec296a5 365 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
753a6f92 366 //char str[65],str2[65];
9cd014a9 367 if ( vinTx.vout.size() > 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
753a6f92 368 {
4d2b7323 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 )
753a6f92 370 {
371 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
372 if ( hentropy == hentropy2 )
373 {
4a827887 374 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
22197277 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));
753a6f92 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 }
48b3eb85 387 } else fprintf(stderr,"hentropy != hentropy2\n");
19be1d65 388 } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid);
d74d791a 389 } //else fprintf(stderr,"notmine or not CC\n");
753a6f92 390 return(0);
391}
392
4d2b7323 393bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
cfea7a46 394{
654c77c8 395 fprintf(stderr,"DiceVerifyTimeout needs to be implemented\n");
4d2b7323 396 return(false);
397}
398
399bool 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];
cfea7a46 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 {
10078e85 409 txid = tx.GetHash();
54519f69 410 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 411 {
10078e85 412 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
413 return eval->Invalid("cant find fundingtxid");
9025093e 414 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
10078e85 415 return eval->Invalid("fundingTx not valid");
4d2b7323 416 fundingPubKey = fundingTx.vout[1].scriptPubKey;
10078e85 417 switch ( funcid )
cfea7a46 418 {
10078e85 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
7eb60411 424 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
10078e85 425 return eval->Invalid("unexpected DiceValidate for createfunding");
426 break;
4d2b7323 427 case 'E': // check sig of vin to match fundingtxid in the 'B' tx
10078e85 428 //vins.*: normal inputs
0d4cfd0b 429 //vout.0: CC vout for locked entropy funds
430 //vout.1: tag to owner address for entropy funds
431 //vout.2: normal change
28b947b8 432 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
433 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
10078e85 434 break;
04cf9f60 435 case 'B':
28b947b8 436 //vin.0: entropy txid from house
437 //vins.1+: normal inputs
0d4cfd0b 438 //vout.0: CC vout for locked entropy
28b947b8 439 //vout.1: CC vout for locked bet
98c6e2e1 440 //vout.2: tag for bettor's address (txfee + odds)
28b947b8 441 //vout.3: change
442 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
4d2b7323 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");
502dad46 461 else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 )
4d2b7323 462 return eval->Invalid("always should find vinofvin.0, but didnt for bet");
3d473d99 463 else if ( vinTx.vin[0].prevout.hash != fundingtxid )
8a2146db 464 {
65ec0508 465 if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
3d473d99 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);
65ec0508 471 ptr0 = (uint8_t *)vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.data();
3d473d99 472 ptr1 = (uint8_t *)fundingPubKey.data();
65ec0508 473 for (i=0; i<vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.size(); i++)
3d473d99 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));
313ee6d7 479 return eval->Invalid("vin1 of entropy tx not fundingPubKey for bet");
3d473d99 480 }
8a2146db 481 }
aec296a5 482 if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1c87477b 483 {
4d2b7323 484 // will only happen for fundingPubKey
fb89b441 485 DiceQueue(iswin,sbits,fundingtxid,txid);
1c87477b 486 }
10078e85 487 break;
5d3d3a87 488 case 'L':
5d3d3a87 489 case 'W':
02409974 490 case 'T':
5ce44e36 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'
7eb60411 495 //vout.1: tag to owner address for entropy funds
4d2b7323 496 preventCCvouts = 1;
497 DiceAmounts(inputs,outputs,cp,eval,tx);
c066bd7b 498 if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 )
4d2b7323 499 return eval->Invalid("vin0 or vin1 normal vin for bet");
c066bd7b 500 else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash )
4d2b7323 501 return eval->Invalid("vin0 != vin1 prevout.hash for bet");
c066bd7b 502 else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
4d2b7323 503 return eval->Invalid("always should find vin.0, but didnt for wlt");
c066bd7b 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' )
4d2b7323 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' )
10078e85 511 {
4d2b7323 512 //vout.0: funding CC to entropy owner
513 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
c066bd7b 514 if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 )
4d2b7323 515 return eval->Invalid("vout[0] != inputs-txfee for loss");
00005381 516 else if ( tx.vout[2].scriptPubKey != fundingPubKey )
c8d1932a 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 }
4d2b7323 521 iswin = -1;
10078e85 522 }
4d2b7323 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");
f3ad9d48 533 else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue )
4d2b7323 534 return eval->Invalid("vout[2] payut mismatch for win/timeout");
c6485d87 535 else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) )
d2c1a2f2 536 {
c6485d87 537 fprintf(stderr,"inputs %.8f != outputs %.8f + %.8f\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[2].nValue/COIN);
4d2b7323 538 return eval->Invalid("CC funds mismatch for win/timeout");
d2c1a2f2 539 }
00005381 540 else if ( tx.vout[3].scriptPubKey != fundingPubKey )
c8d1932a 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 }
4d2b7323 545 iswin = (funcid == 'W');
546 }
547 if ( iswin != 0 )
548 {
ef979a50 549 //char str[65],str2[65];
53f6ead6 550 entropy = DiceGetEntropy(vinTx,'B');
e7479ef8 551 vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32);
ef979a50 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));
53f6ead6 554 winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy);
4d2b7323 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");
10078e85 560 break;
cfea7a46 561 }
562 }
10078e85 563 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
cfea7a46 564 }
10078e85 565 return(true);
cfea7a46 566}
cfea7a46 567
4d2b7323 568uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
cfea7a46 569{
54519f69 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;
cfea7a46 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;
10078e85 577 vout = (int32_t)it->first.index;
93ac96fa 578 if ( it->second.satoshis < 1000000 )
c2342f7b 579 continue;
ef979a50 580 //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
10078e85 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;
079be98a 586 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
cfea7a46 587 {
54519f69 588 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 589 {
4d2b7323 590 if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
10078e85 591 {
4d2b7323 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;
10078e85 598 }
10078e85 599 } else fprintf(stderr,"null funcid\n");
cfea7a46 600 }
601 }
602 return(totalinputs);
603}
604
4d2b7323 605uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid)
10078e85 606{
e9c75366 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;
10078e85 608 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
a1b43551 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);
4d2b7323 613 GetCCaddress(cp,coinaddr,dicepk);
10078e85 614 SetCCunspents(unspentOutputs,coinaddr);
04cf9f60 615 entropyval = 0;
10078e85 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;
079be98a 620 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
10078e85 621 {
4d2b7323 622 //char str[65],str2[65];
54519f69 623 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
10078e85 624 {
625 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
626 {
4d2b7323 627 if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
04cf9f60 628 {
4d2b7323 629 if ( funcid != 'F' && funcid != 'T' )
14debc6d 630 {
4d2b7323 631 n++;
7e988759 632 fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN);
14debc6d 633 }
10078e85 634 totalinputs += nValue;
14debc6d 635 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
04cf9f60 636 {
80314218 637 fprintf(stderr,"check first\n");
14debc6d 638 if ( fundingPubKey == tx.vout[1].scriptPubKey )
4d2b7323 639 {
28e42c57 640 if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash )
4d2b7323 641 {
f7af30ae 642 if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 )
8eef6813 643 {
4b4113dd 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());
8eef6813 645 continue;
646 }
af42329c 647 if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
14debc6d 648 {
6afd491b 649 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
af42329c 650 ptr0 = (uint8_t *)vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.data();
385f6db0 651 ptr1 = (uint8_t *)fundingPubKey.data();
af42329c 652 for (i=0; i<vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.size(); i++)
385f6db0 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]);
6afd491b 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));
14debc6d 658 continue;
659 }
2ac06413 660 } //else fprintf(stderr,"not E or is funding\n");
14debc6d 661 entropytxid = txid;
662 entropyval = tx.vout[0].nValue;
663 first = 1;
04a4df80 664 }
3f01899a 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 }
04cf9f60 677 }
1fadc799 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);
d4c504f0 679 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
06811319 680 } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
10078e85 681 }
682 }
4d2b7323 683 fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN);
10078e85 684 return(totalinputs);
685}
686
aec296a5 687bool 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)
10078e85 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);
3c3451b8 692 SetCCtxids(txids,cp->normaladdr);
aec296a5 693 if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
694 {
83da810c 695 if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
aec296a5 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 }
10078e85 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;
a03bb7fa 709 if ( fundingtxid != zeroid && txid != fundingtxid )
710 continue;
10078e85 711 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
712 {
9025093e 713 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
10078e85 714 {
715 if ( sbits == refsbits )
a03bb7fa 716 {
aec296a5 717 fundingPubKey = tx.vout[1].scriptPubKey;
a03bb7fa 718 fundingtxid = txid;
10078e85 719 return(true);
a03bb7fa 720 }
10078e85 721 }
722 }
723 }
724 return(false);
725}
726
46a40047 727struct 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)
a03bb7fa 728{
28500c30 729 struct CCcontract_info *cp; int32_t cmpflag;
a03bb7fa 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;
28500c30 739 if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
a03bb7fa 740 {
97b0bc3a 741 fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
a03bb7fa 742 return(0);
743 }
744 return(cp);
745}
746
10078e85 747UniValue DiceInfo(uint256 diceid)
748{
f516779c 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;
10078e85 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 }
9025093e 756 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
10078e85 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));
d0d96ab0 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));
9025093e 772 result.push_back(Pair("timeoutblocks",timeoutblocks));
f516779c 773 cp = CCinit(&C,EVAL_DICE);
774 dicepk = GetUnspendable(cp,0);
688d3ac4 775 funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
f516779c 776 sprintf(numstr,"%.8f",(double)funding/COIN);
10078e85 777 result.push_back(Pair("funding",numstr));
778 return(result);
779}
780
781UniValue DiceList()
cfea7a46 782{
9025093e 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];
10078e85 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 {
9025093e 791 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
10078e85 792 {
793 result.push_back(uint256_str(str,txid));
794 }
795 }
796 }
797 return(result);
798}
799
9025093e 800std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
05177cfb 801{
aec296a5 802 CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
9025093e 803 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
05177cfb 804 {
805 fprintf(stderr,"negative parameter error\n");
806 return(0);
807 }
aec296a5 808 memset(&zero,0,sizeof(zero));
809 if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
05177cfb 810 return(0);
654c77c8 811 if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 )
10078e85 812 {
813 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
32e3be87 814 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
10078e85 815 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
9025093e 816 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
10078e85 817 }
818 fprintf(stderr,"cant find enough inputs\n");
819 return(0);
820}
821
822std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
823{
aec296a5 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;
10078e85 825 if ( amount < 0 )
826 {
827 fprintf(stderr,"negative parameter error\n");
828 return(0);
829 }
aec296a5 830 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
10078e85 831 return(0);
aec296a5 832 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
ef979a50 833 if ( 0 )
792cef06 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 }
aec296a5 845 if ( scriptPubKey == fundingPubKey )
10078e85 846 {
654c77c8 847 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 )
aec296a5 848 {
849 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
850 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
7377f6ea 851 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
aec296a5 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");
cfea7a46 855 return(0);
856}
857
7347a801 858std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
5d3d3a87 859{
aec296a5 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;
7347a801 861 if ( bet < 0 || odds < 1 )
5d3d3a87 862 {
863 fprintf(stderr,"negative parameter error\n");
864 return(0);
865 }
aec296a5 866 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
7347a801 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 }
7eb60411 873 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
7347a801 874 {
079be98a 875 if ( myIsutxo_spentinmempool(entropytxid,0) != 0 )
654c77c8 876 {
877 fprintf(stderr,"entropy txid is spent\n");
878 return(0);
879 }
7347a801 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));
afef48c0 887 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
7347a801 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
7e988759 896std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
7347a801 897{
00005381 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;
cfdc3770 899 *resultp = 0;
d74d791a 900 //char str[65]; fprintf(stderr,"DiceBetFinish.%s %s\n",planstr,uint256_str(str,bettxid));
aec296a5 901 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
07009479 902 {
903 fprintf(stderr,"Diceinit error\n");
113adfd4 904 return("0");
07009479 905 }
00005381 906 fundingpk = DiceFundingPk(fundingPubKey);
aec296a5 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");
113adfd4 913 return("0");
aec296a5 914 }
915 }
c066bd7b 916 if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 )
917 {
918 fprintf(stderr,"no txfee inputs for win/lose\n");
919 return("0");
920 }
a03bb7fa 921 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
5d3d3a87 922 {
a03bb7fa 923 bettorentropy = DiceGetEntropy(betTx,'B');
4d2b7323 924 if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
a03bb7fa 925 {
7e988759 926 winlosetimeout = iswin;
02409974 927 if ( iswin == winlosetimeout )
a03bb7fa 928 {
079be98a 929 if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 )
654c77c8 930 {
931 fprintf(stderr,"bettxid already spent\n");
92b48fee 932 return("0");
654c77c8 933 }
d74d791a 934 //fprintf(stderr,"iswin.%d matches\n",iswin);
7eb60411 935 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
936 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
a03bb7fa 937 if ( iswin == 0 )
7eb60411 938 {
02409974 939 funcid = 'T';
4d2b7323 940 if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
941 {
942 fprintf(stderr,"timeout is not supported yet\n");
92b48fee 943 return("0");
4d2b7323 944 }
945 else
946 {
947 hentropy = hentropyproof = zeroid;
948 iswin = 1;
949 }
7eb60411 950 }
4d2b7323 951 if ( iswin > 0 )
7eb60411 952 {
a03bb7fa 953 funcid = 'W';
7eb60411 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);
113adfd4 958 return("0");
7eb60411 959 }
960 CCchange = betTx.vout[0].nValue;
f3ad9d48 961 fundsneeded = txfee + odds*betTx.vout[1].nValue;
4d2b7323 962 if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60)) > 0 )
7eb60411 963 {
3454564b 964 if ( inputs > fundsneeded )
965 CCchange += (inputs - fundsneeded);
7eb60411 966 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
04a4df80 967 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
f3ad9d48 968 mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
7eb60411 969 }
970 else
971 {
972 fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
113adfd4 973 return("0");
7eb60411 974 }
975 }
976 else
a03bb7fa 977 {
7eb60411 978 funcid = 'L';
c066bd7b 979 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk));
04a4df80 980 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
7eb60411 981 }
4d2b7323 982 if ( winlosetimeout != 0 )
983 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
cfdc3770 984 *resultp = 1;
d74d791a 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));
00005381 987 return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
e0ea3b3e 988 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
48b3eb85 989 }
990 else
991 {
d74d791a 992 *resultp = -1;
48b3eb85 993 fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
994 return("0");
995 }
a03bb7fa 996 }
d74d791a 997 *resultp = -1;
598e88ce 998 fprintf(stderr,"couldnt find bettx or entropytx\n");
113adfd4 999 return("0");
5d3d3a87 1000}
1001
7e988759 1002double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid)
1003{
fba740ea 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;
7e988759 1005 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1006 {
1007 fprintf(stderr,"Diceinit error\n");
6113aef2 1008 return(0.);
7e988759 1009 }
1010 fundingpk = DiceFundingPk(fundingPubKey);
f5d3217a 1011 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
510d6d5c 1012 GetCCaddress(cp,coinaddr,dicepk);
7e988759 1013 if ( bettxid == zeroid ) // scan
1014 {
510d6d5c 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 {
50854c78 1023 if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' )
510d6d5c 1024 {
22197277 1025 res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey);
510d6d5c 1026 if ( result > 0 )
096cfeb8 1027 {
510d6d5c 1028 mySendrawtransaction(res);
096cfeb8 1029 n++;
1030 }
510d6d5c 1031 }
1032 }
1033 }
d4c70c4f 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 }
096cfeb8 1042 return(n);
7e988759 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);
d74d791a 1058 if ( result > 0 )
7e988759 1059 {
d74d791a 1060 mySendrawtransaction(res);
1061 sleep(1);
1062 if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
7e988759 1063 {
d74d791a 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.);
7e988759 1073 }
1074 return(0.);
1075}
This page took 0.268535 seconds and 4 git commands to generate.