]> Git Repo - VerusCoin.git/blame - src/cc/dice.cpp
Backwards compatibility breaking changes
[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
dec9218b 84#include "../compat/endian.h"
ae748eaf 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
7300a1c1 316uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
cfea7a46 317{
8608399c 318 char destaddr[64]; uint8_t funcid; int32_t numvouts; uint64_t sbits; uint256 fundingtxid,hash,proof;
cfea7a46 319 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
320 {
8608399c 321 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 && (numvouts= tx.vout.size()) > 0 )
7300a1c1 322 {
8608399c 323 if ( (funcid= DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits && ((funcid == 'F' && tx.GetHash() == reffundingtxid) || fundingtxid == reffundingtxid) )
7300a1c1 324 return(tx.vout[v].nValue);
325 }
cfea7a46 326 }
327 return(0);
328}
329
7300a1c1 330int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t refsbits,uint256 reffundingtxid)
cfea7a46 331{
4d2b7323 332 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis;
cfea7a46 333 numvins = tx.vin.size();
334 numvouts = tx.vout.size();
60301f5e 335 inputs = outputs = 0;
cfea7a46 336 for (i=0; i<numvins; i++)
337 {
cfea7a46 338 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
339 {
cfea7a46 340 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
10078e85 341 return eval->Invalid("always should find vin, but didnt");
cfea7a46 342 else
343 {
7300a1c1 344 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
cfea7a46 345 inputs += assetoshis;
346 }
347 }
348 }
349 for (i=0; i<numvouts; i++)
350 {
351 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
7300a1c1 352 if ( (assetoshis= IsDicevout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
cfea7a46 353 outputs += assetoshis;
354 }
4d2b7323 355 return(inputs - outputs);
cfea7a46 356}
357
1c87477b 358bool DiceIsmine(const CScript scriptPubKey)
359{
360 char destaddr[64],myaddr[64];
361 Getscriptaddress(destaddr,scriptPubKey);
ca05cbaa 362 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
1c87477b 363 return(strcmp(destaddr,myaddr) == 0);
364}
365
aec296a5 366int32_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 367{
aec296a5 368 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
753a6f92 369 //char str[65],str2[65];
9cd014a9 370 if ( vinTx.vout.size() > 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
753a6f92 371 {
4d2b7323 372 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 373 {
374 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
375 if ( hentropy == hentropy2 )
376 {
4a827887 377 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
22197277 378 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 379 //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);
380 if ( winnings == 0 )
381 {
382 // queue 'L' losing tx
383 return(-1);
384 }
385 else
386 {
387 // queue 'W' winning tx
388 return(1);
389 }
48b3eb85 390 } else fprintf(stderr,"hentropy != hentropy2\n");
19be1d65 391 } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid);
d74d791a 392 } //else fprintf(stderr,"notmine or not CC\n");
753a6f92 393 return(0);
394}
395
4d2b7323 396bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
cfea7a46 397{
da07d84b 398 int32_t numblocks;
399 if ( CCduration(numblocks,betTx.GetHash()) <= 0 )
400 return(false);
401 return(numblocks >= timeoutblocks);
4d2b7323 402}
403
404bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
405{
406 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 407 numvins = tx.vin.size();
408 numvouts = tx.vout.size();
409 preventCCvins = preventCCvouts = -1;
410 if ( numvouts < 1 )
411 return eval->Invalid("no vouts");
412 else
413 {
10078e85 414 txid = tx.GetHash();
54519f69 415 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 416 {
10078e85 417 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
418 return eval->Invalid("cant find fundingtxid");
9025093e 419 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
10078e85 420 return eval->Invalid("fundingTx not valid");
4d2b7323 421 fundingPubKey = fundingTx.vout[1].scriptPubKey;
10078e85 422 switch ( funcid )
cfea7a46 423 {
10078e85 424 case 'F':
425 //vins.*: normal inputs
426 //vout.0: CC vout for funding
427 //vout.1: normal marker vout for easy searching
428 //vout.2: normal change
7eb60411 429 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
10078e85 430 return eval->Invalid("unexpected DiceValidate for createfunding");
431 break;
4d2b7323 432 case 'E': // check sig of vin to match fundingtxid in the 'B' tx
10078e85 433 //vins.*: normal inputs
0d4cfd0b 434 //vout.0: CC vout for locked entropy funds
435 //vout.1: tag to owner address for entropy funds
436 //vout.2: normal change
28b947b8 437 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
438 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
10078e85 439 break;
04cf9f60 440 case 'B':
28b947b8 441 //vin.0: entropy txid from house
442 //vins.1+: normal inputs
0d4cfd0b 443 //vout.0: CC vout for locked entropy
28b947b8 444 //vout.1: CC vout for locked bet
98c6e2e1 445 //vout.2: tag for bettor's address (txfee + odds)
28b947b8 446 //vout.3: change
447 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
4d2b7323 448 preventCCvouts = 2;
449 preventCCvins = 1;
450 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
451 return eval->Invalid("vin.0 is normal for bet");
452 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
453 return eval->Invalid("vout.0 is normal for bet");
454 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
455 return eval->Invalid("vout.1 is normal for bet");
456 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
457 return eval->Invalid("always should find vin.0, but didnt for bet");
458 else if ( vinTx.vout[1].scriptPubKey != fundingPubKey )
459 return eval->Invalid("entropy tx not fundingPubKey for bet");
460 else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 )
461 return eval->Invalid("vout[0] != entropy nValue for bet");
462 else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 )
463 return eval->Invalid("vout[1] constrain violation for bet");
99c090d9 464 else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue < txfee )
4d2b7323 465 return eval->Invalid("vout[2] nValue violation for bet");
502dad46 466 else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 )
4d2b7323 467 return eval->Invalid("always should find vinofvin.0, but didnt for bet");
3d473d99 468 else if ( vinTx.vin[0].prevout.hash != fundingtxid )
8a2146db 469 {
65ec0508 470 if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
3d473d99 471 {
472 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
473 fprintf(stderr,"bidTx.%s\n",uint256_str(str,txid));
474 fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n);
475 fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n);
65ec0508 476 ptr0 = (uint8_t *)vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.data();
3d473d99 477 ptr1 = (uint8_t *)fundingPubKey.data();
65ec0508 478 for (i=0; i<vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey.size(); i++)
3d473d99 479 fprintf(stderr,"%02x",ptr0[i]);
480 fprintf(stderr," script vs ");
481 for (i=0; i<fundingPubKey.size(); i++)
482 fprintf(stderr,"%02x",ptr1[i]);
483 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 484 return eval->Invalid("vin1 of entropy tx not fundingPubKey for bet");
3d473d99 485 }
8a2146db 486 }
aec296a5 487 if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1c87477b 488 {
4d2b7323 489 // will only happen for fundingPubKey
fb89b441 490 DiceQueue(iswin,sbits,fundingtxid,txid);
1c87477b 491 }
10078e85 492 break;
d043ec4f 493 // make sure all funding txid are from matching sbits and fundingtxid!!
5d3d3a87 494 case 'L':
5d3d3a87 495 case 'W':
02409974 496 case 'T':
5ce44e36 497 //vin.0: normal input
498 //vin.1: betTx CC vout.0 entropy from bet
499 //vin.2: betTx CC vout.1 bet amount from bet
500 //vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 501 //vout.1: tag to owner address for entropy funds
4d2b7323 502 preventCCvouts = 1;
7300a1c1 503 DiceAmounts(inputs,outputs,cp,eval,tx,sbits,fundingtxid);
c066bd7b 504 if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 )
4d2b7323 505 return eval->Invalid("vin0 or vin1 normal vin for bet");
c066bd7b 506 else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash )
4d2b7323 507 return eval->Invalid("vin0 != vin1 prevout.hash for bet");
c066bd7b 508 else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
4d2b7323 509 return eval->Invalid("always should find vin.0, but didnt for wlt");
c066bd7b 510 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 511 return eval->Invalid("not betTx for vin0/1 for wlt");
512 else if ( sbits != vinsbits || fundingtxid != vinfundingtxid )
513 return eval->Invalid("sbits or fundingtxid mismatch for wlt");
514 else if ( fundingPubKey != tx.vout[1].scriptPubKey )
515 return eval->Invalid("tx.vout[1] != fundingPubKey for wlt");
516 if ( funcid == 'L' )
10078e85 517 {
4d2b7323 518 //vout.0: funding CC to entropy owner
519 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
c066bd7b 520 if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 )
4d2b7323 521 return eval->Invalid("vout[0] != inputs-txfee for loss");
00005381 522 else if ( tx.vout[2].scriptPubKey != fundingPubKey )
c8d1932a 523 {
524 if ( tx.vout[2].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[2].scriptPubKey.data())[0] != 0x6a )
525 return eval->Invalid("vout[2] not send to fundingPubKey for loss");
526 }
4d2b7323 527 iswin = -1;
10078e85 528 }
4d2b7323 529 else
530 {
531 //vout.0: funding CC change to entropy owner
532 //vout.2: normal output to bettor's address
533 //vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
534 odds = vinTx.vout[2].nValue - txfee;
535 if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
536 return eval->Invalid("vout[0] != inputs-txfee for win/timeout");
537 else if ( tx.vout[2].scriptPubKey != vinTx.vout[2].scriptPubKey )
538 return eval->Invalid("vout[2] scriptPubKey mismatch for win/timeout");
f3ad9d48 539 else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue )
4d2b7323 540 return eval->Invalid("vout[2] payut mismatch for win/timeout");
c6485d87 541 else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) )
d2c1a2f2 542 {
c6485d87 543 fprintf(stderr,"inputs %.8f != outputs %.8f + %.8f\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[2].nValue/COIN);
4d2b7323 544 return eval->Invalid("CC funds mismatch for win/timeout");
d2c1a2f2 545 }
00005381 546 else if ( tx.vout[3].scriptPubKey != fundingPubKey )
c8d1932a 547 {
548 if ( tx.vout[3].scriptPubKey.size() == 0 || ((uint8_t *)tx.vout[3].scriptPubKey.data())[0] != 0x6a )
549 return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout");
550 }
4d2b7323 551 iswin = (funcid == 'W');
552 }
553 if ( iswin != 0 )
554 {
ef979a50 555 //char str[65],str2[65];
53f6ead6 556 entropy = DiceGetEntropy(vinTx,'B');
e7479ef8 557 vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32);
ef979a50 558 //fprintf(stderr,"calculated house hentropy.%s\n",uint256_str(str,hash));
559 //fprintf(stderr,"verify house entropy %s vs bettor %s\n",uint256_str(str,proof),uint256_str(str2,entropy));
53f6ead6 560 winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy);
4d2b7323 561 if ( (winnings == 0 && iswin > 0) || (winnings > 0 && iswin < 0) )
562 return eval->Invalid("DiceCalc mismatch for win/loss");
563 }
564 else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 )
565 return eval->Invalid("invalid timeout claim for timeout");
10078e85 566 break;
cfea7a46 567 }
568 }
10078e85 569 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
cfea7a46 570 }
10078e85 571 return(true);
cfea7a46 572}
cfea7a46 573
d043ec4f 574uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
cfea7a46 575{
54519f69 576 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 577 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
578 GetCCaddress(cp,coinaddr,pk);
579 SetCCunspents(unspentOutputs,coinaddr);
580 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
581 {
582 txid = it->first.txhash;
10078e85 583 vout = (int32_t)it->first.index;
93ac96fa 584 if ( it->second.satoshis < 1000000 )
c2342f7b 585 continue;
ef979a50 586 //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
10078e85 587 for (j=0; j<mtx.vin.size(); j++)
588 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
589 break;
590 if ( j != mtx.vin.size() )
591 continue;
079be98a 592 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
cfea7a46 593 {
944aed48 594 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 595 {
944aed48 596 char str[65],sstr[16];
597 unstringbits(sstr,sbits);
a5a2c7cb 598 fprintf(stderr,"(%c) %.8f %s %s\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid));
944aed48 599 if ( sbits == refsbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
10078e85 600 {
d043ec4f 601 if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
602 {
603 if ( total != 0 && maxinputs != 0 )
604 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
605 totalinputs += it->second.satoshis;
606 n++;
607 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
608 break;
609 }
10078e85 610 }
10078e85 611 } else fprintf(stderr,"null funcid\n");
cfea7a46 612 }
613 }
614 return(totalinputs);
615}
616
278a61fd 617int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid)
10078e85 618{
278a61fd 619 char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0; uint8_t funcid;
10078e85 620 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
a1b43551 621 if ( GetTransaction(reffundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 )
622 {
623 fundingPubKey = tx.vout[1].scriptPubKey;
624 } else return(0);
4d2b7323 625 GetCCaddress(cp,coinaddr,dicepk);
10078e85 626 SetCCunspents(unspentOutputs,coinaddr);
04cf9f60 627 entropyval = 0;
10078e85 628 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
629 {
630 txid = it->first.txhash;
631 vout = (int32_t)it->first.index;
079be98a 632 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
10078e85 633 {
4d2b7323 634 //char str[65],str2[65];
54519f69 635 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
10078e85 636 {
637 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
638 {
7300a1c1 639 if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
04cf9f60 640 {
9a017060 641 fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN);
4d2b7323 642 if ( funcid != 'F' && funcid != 'T' )
643 n++;
10078e85 644 totalinputs += nValue;
14debc6d 645 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
04cf9f60 646 {
80314218 647 fprintf(stderr,"check first\n");
14debc6d 648 if ( fundingPubKey == tx.vout[1].scriptPubKey )
4d2b7323 649 {
28e42c57 650 if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash )
4d2b7323 651 {
f7af30ae 652 if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 )
8eef6813 653 {
4b4113dd 654 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 655 continue;
656 }
af42329c 657 if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
14debc6d 658 {
6afd491b 659 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
af42329c 660 ptr0 = (uint8_t *)vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.data();
385f6db0 661 ptr1 = (uint8_t *)fundingPubKey.data();
af42329c 662 for (i=0; i<vinTx.vout[tx.vin[0].prevout.n].scriptPubKey.size(); i++)
385f6db0 663 fprintf(stderr,"%02x",ptr0[i]);
664 fprintf(stderr," script vs ");
665 for (i=0; i<fundingPubKey.size(); i++)
666 fprintf(stderr,"%02x",ptr1[i]);
6afd491b 667 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 668 continue;
669 }
2ac06413 670 } //else fprintf(stderr,"not E or is funding\n");
14debc6d 671 entropytxid = txid;
672 entropyval = tx.vout[0].nValue;
673 first = 1;
04a4df80 674 }
3f01899a 675 else
676 {
677 uint8_t *ptr0,*ptr1; int32_t i; char str[65];
678 ptr0 = (uint8_t *)tx.vout[1].scriptPubKey.data();
679 ptr1 = (uint8_t *)fundingPubKey.data();
680 for (i=0; i<tx.vout[1].scriptPubKey.size(); i++)
681 fprintf(stderr,"%02x",ptr0[i]);
682 fprintf(stderr," script vs ");
683 for (i=0; i<fundingPubKey.size(); i++)
684 fprintf(stderr,"%02x",ptr1[i]);
685 fprintf(stderr," (%c) tx vin.%d fundingPubKey mismatch %s\n",funcid,tx.vin[0].prevout.n,uint256_str(str,tx.vin[0].prevout.hash));
686 }
04cf9f60 687 }
1fadc799 688 } 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 689 } //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
06811319 690 } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
10078e85 691 }
692 }
4d2b7323 693 fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN);
10078e85 694 return(totalinputs);
695}
696
aec296a5 697bool 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 698{
699 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
700 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
701 GetCCaddress(cp,CCaddr,dicepk);
3c3451b8 702 SetCCtxids(txids,cp->normaladdr);
aec296a5 703 if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
704 {
83da810c 705 if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
aec296a5 706 {
707 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
708 {
709 fundingPubKey = tx.vout[1].scriptPubKey;
710 return(true);
711 }
712 }
713 return(false);
714 }
10078e85 715 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
716 {
717 //int height = it->first.blockHeight;
718 txid = it->first.txhash;
a03bb7fa 719 if ( fundingtxid != zeroid && txid != fundingtxid )
720 continue;
10078e85 721 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
722 {
9025093e 723 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
10078e85 724 {
725 if ( sbits == refsbits )
a03bb7fa 726 {
aec296a5 727 fundingPubKey = tx.vout[1].scriptPubKey;
a03bb7fa 728 fundingtxid = txid;
10078e85 729 return(true);
a03bb7fa 730 }
10078e85 731 }
732 }
733 }
734 return(false);
735}
736
46a40047 737struct 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 738{
28500c30 739 struct CCcontract_info *cp; int32_t cmpflag;
a03bb7fa 740 cp = CCinit(C,EVAL_DICE);
741 if ( txfee == 0 )
742 txfee = 10000;
743 mypk = pubkey2pk(Mypubkey());
744 dicepk = GetUnspendable(cp,0);
745 sbits = stringbits(planstr);
746 if ( reffundingtxid == zeroid )
747 cmpflag = 0;
748 else cmpflag = 1;
28500c30 749 if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
a03bb7fa 750 {
97b0bc3a 751 fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
a03bb7fa 752 return(0);
753 }
754 return(cp);
755}
756
10078e85 757UniValue DiceInfo(uint256 diceid)
758{
f516779c 759 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 760 if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
761 {
762 fprintf(stderr,"cant find fundingtxid\n");
763 result.push_back(Pair("error","cant find fundingtxid"));
764 return(result);
765 }
9025093e 766 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
10078e85 767 {
768 fprintf(stderr,"fundingtxid isnt dice creation txid\n");
769 result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
770 return(result);
771 }
772 result.push_back(Pair("result","success"));
773 result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
774 unstringbits(str,sbits);
775 result.push_back(Pair("name",str));
776 result.push_back(Pair("sbits",sbits));
d0d96ab0 777 sprintf(numstr,"%.8f",(double)minbet/COIN);
778 result.push_back(Pair("minbet",numstr));
779 sprintf(numstr,"%.8f",(double)maxbet/COIN);
780 result.push_back(Pair("maxbet",numstr));
781 result.push_back(Pair("maxodds",maxodds));
9025093e 782 result.push_back(Pair("timeoutblocks",timeoutblocks));
f516779c 783 cp = CCinit(&C,EVAL_DICE);
784 dicepk = GetUnspendable(cp,0);
688d3ac4 785 funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
f516779c 786 sprintf(numstr,"%.8f",(double)funding/COIN);
10078e85 787 result.push_back(Pair("funding",numstr));
788 return(result);
789}
790
791UniValue DiceList()
cfea7a46 792{
9025093e 793 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 794 cp = CCinit(&C,EVAL_DICE);
795 SetCCtxids(addressIndex,cp->normaladdr);
796 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
797 {
798 txid = it->first.txhash;
799 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
800 {
9025093e 801 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
10078e85 802 {
803 result.push_back(uint256_str(str,txid));
804 }
805 }
806 }
807 return(result);
808}
809
9025093e 810std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
05177cfb 811{
aec296a5 812 CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
9025093e 813 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
05177cfb 814 {
815 fprintf(stderr,"negative parameter error\n");
1ed46fb8 816 return("");
05177cfb 817 }
aec296a5 818 memset(&zero,0,sizeof(zero));
819 if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
1ed46fb8 820 return("");
654c77c8 821 if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 )
10078e85 822 {
823 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
32e3be87 824 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
10078e85 825 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
9025093e 826 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
10078e85 827 }
828 fprintf(stderr,"cant find enough inputs\n");
1ed46fb8 829 return("");
10078e85 830}
831
832std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
833{
aec296a5 834 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 835 if ( amount < 0 )
836 {
837 fprintf(stderr,"negative parameter error\n");
1ed46fb8 838 return("");
10078e85 839 }
aec296a5 840 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1ed46fb8 841 return("");
aec296a5 842 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
ef979a50 843 if ( 0 )
792cef06 844 {
845 uint8_t *ptr0,*ptr1; int32_t i;
846 ptr0 = (uint8_t *)scriptPubKey.data();
847 ptr1 = (uint8_t *)fundingPubKey.data();
848 for (i=0; i<35; i++)
849 fprintf(stderr,"%02x",ptr0[i]);
850 fprintf(stderr," script vs ");
851 for (i=0; i<35; i++)
852 fprintf(stderr,"%02x",ptr1[i]);
853 fprintf(stderr," funding\n");
854 }
aec296a5 855 if ( scriptPubKey == fundingPubKey )
10078e85 856 {
654c77c8 857 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 )
aec296a5 858 {
859 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
860 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
7377f6ea 861 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
aec296a5 862 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
863 } else fprintf(stderr,"cant find enough inputs\n");
864 } else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
1ed46fb8 865 return("");
cfea7a46 866}
867
7347a801 868std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
5d3d3a87 869{
aec296a5 870 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 871 if ( bet < 0 || odds < 1 )
5d3d3a87 872 {
873 fprintf(stderr,"negative parameter error\n");
1ed46fb8 874 return("");
5d3d3a87 875 }
aec296a5 876 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1ed46fb8 877 return("");
7347a801 878 if ( bet < minbet || bet > maxbet || odds > maxodds )
879 {
880 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);
1ed46fb8 881 return("");
7347a801 882 }
7eb60411 883 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
7347a801 884 {
079be98a 885 if ( myIsutxo_spentinmempool(entropytxid,0) != 0 )
654c77c8 886 {
887 fprintf(stderr,"entropy txid is spent\n");
1ed46fb8 888 return("");
654c77c8 889 }
7347a801 890 mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
891 if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
892 {
893 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
894 mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
895 mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
896 mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
afef48c0 897 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
7347a801 898 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
899 }
900 if ( entropyval == 0 && funding != 0 )
901 fprintf(stderr,"cant find dice entropy inputs\n");
902 else fprintf(stderr,"cant find dice inputs\n");
1ed46fb8 903 return("");
7347a801 904}
905
7e988759 906std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
7347a801 907{
a4a96df9 908 CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid=0; int32_t iswin=0; uint64_t entropyval,sbits;
cfdc3770 909 *resultp = 0;
d74d791a 910 //char str[65]; fprintf(stderr,"DiceBetFinish.%s %s\n",planstr,uint256_str(str,bettxid));
aec296a5 911 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
07009479 912 {
913 fprintf(stderr,"Diceinit error\n");
1ed46fb8 914 return("");
07009479 915 }
00005381 916 fundingpk = DiceFundingPk(fundingPubKey);
aec296a5 917 if ( winlosetimeout != 0 )
918 {
919 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
920 if ( scriptPubKey != fundingPubKey )
921 {
b0becd6e 922 //fprintf(stderr,"only dice fund creator can submit winner or loser\n");
8e3c66ca 923 winlosetimeout = 0;
aec296a5 924 }
925 }
c066bd7b 926 if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 )
927 {
928 fprintf(stderr,"no txfee inputs for win/lose\n");
1ed46fb8 929 return("");
c066bd7b 930 }
a03bb7fa 931 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
5d3d3a87 932 {
a03bb7fa 933 bettorentropy = DiceGetEntropy(betTx,'B');
4d2b7323 934 if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
a03bb7fa 935 {
b0becd6e 936 if ( winlosetimeout != 0 )
937 winlosetimeout = iswin;
02409974 938 if ( iswin == winlosetimeout )
a03bb7fa 939 {
079be98a 940 if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 )
654c77c8 941 {
942 fprintf(stderr,"bettxid already spent\n");
1ed46fb8 943 return("");
654c77c8 944 }
d74d791a 945 //fprintf(stderr,"iswin.%d matches\n",iswin);
7eb60411 946 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
947 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
a03bb7fa 948 if ( iswin == 0 )
7eb60411 949 {
02409974 950 funcid = 'T';
4d2b7323 951 if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
952 {
1ed46fb8 953 return("");
4d2b7323 954 }
955 else
956 {
957 hentropy = hentropyproof = zeroid;
958 iswin = 1;
959 }
7eb60411 960 }
4d2b7323 961 if ( iswin > 0 )
7eb60411 962 {
ebe7e27a 963 if ( funcid != 'T' )
964 funcid = 'W';
7eb60411 965 odds = (betTx.vout[2].nValue - txfee);
966 if ( odds < 1 || odds > maxodds )
967 {
968 fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
1ed46fb8 969 return("");
7eb60411 970 }
2b4374d7 971 CCchange = betTx.vout[0].nValue + betTx.vout[1].nValue;
1da742e7 972 fundsneeded = txfee + (odds+1)*betTx.vout[1].nValue;
973 if ( CCchange >= fundsneeded )
974 CCchange -= fundsneeded;
0e03d559 975 else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 )
7eb60411 976 {
3454564b 977 if ( inputs > fundsneeded )
978 CCchange += (inputs - fundsneeded);
7eb60411 979 }
980 else
981 {
982 fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
1ed46fb8 983 return("");
7eb60411 984 }
0e03d559 985 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
986 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
987 mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
7eb60411 988 }
989 else
a03bb7fa 990 {
7eb60411 991 funcid = 'L';
c066bd7b 992 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk));
04a4df80 993 mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
7eb60411 994 }
4d2b7323 995 if ( winlosetimeout != 0 )
996 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
cfdc3770 997 *resultp = 1;
d74d791a 998 //char str[65],str2[65];
999 //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy));
00005381 1000 return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
e0ea3b3e 1001 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
48b3eb85 1002 }
1003 else
1004 {
d74d791a 1005 *resultp = -1;
48b3eb85 1006 fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
1ed46fb8 1007 return("");
48b3eb85 1008 }
a03bb7fa 1009 }
d74d791a 1010 *resultp = -1;
598e88ce 1011 fprintf(stderr,"couldnt find bettx or entropytx\n");
1ed46fb8 1012 return("");
5d3d3a87 1013}
1014
7e988759 1015double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid)
1016{
fba740ea 1017 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 1018 if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
1019 {
1020 fprintf(stderr,"Diceinit error\n");
6113aef2 1021 return(0.);
7e988759 1022 }
1023 fundingpk = DiceFundingPk(fundingPubKey);
f5d3217a 1024 scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
510d6d5c 1025 GetCCaddress(cp,coinaddr,dicepk);
7e988759 1026 if ( bettxid == zeroid ) // scan
1027 {
510d6d5c 1028 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
1029 SetCCunspents(unspentOutputs,coinaddr);
1030 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
1031 {
1032 txid = it->first.txhash;
1033 vout = (int32_t)it->first.index;
1034 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
1035 {
50854c78 1036 if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' )
510d6d5c 1037 {
22197277 1038 res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey);
510d6d5c 1039 if ( result > 0 )
096cfeb8 1040 {
510d6d5c 1041 mySendrawtransaction(res);
096cfeb8 1042 n++;
1043 }
510d6d5c 1044 }
1045 }
1046 }
d4c70c4f 1047 if ( scriptPubKey == fundingPubKey )
1048 {
1049 for (i=0; i<=n; i++)
1050 {
1051 res = DiceAddfunding(txfee,planstr,fundingtxid,COIN);
8132e087 1052 fprintf(stderr,"ENTROPY tx:\n");
d4c70c4f 1053 mySendrawtransaction(res);
1054 }
1055 }
096cfeb8 1056 return(n);
7e988759 1057 }
1058 else
1059 {
1060 if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
1061 {
6ca2e998 1062 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() > 2 )
7e988759 1063 {
6ca2e998 1064 if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
7e988759 1065 return(0.);
1066 else return((double)spenttx.vout[2].nValue/COIN);
1067 } else return(0.);
1068 }
1069 else if ( scriptPubKey == fundingPubKey )
1070 res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,1);
1071 else res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,0);
d74d791a 1072 if ( result > 0 )
7e988759 1073 {
d74d791a 1074 mySendrawtransaction(res);
1075 sleep(1);
1076 if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
7e988759 1077 {
6ca2e998 1078 if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() >= 2 )
d74d791a 1079 {
6ca2e998 1080 if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
1081 //if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a )
d74d791a 1082 return(0.);
1083 else return((double)spenttx.vout[2].nValue/COIN);
1084 } else return(0.);
1085 }
1086 fprintf(stderr,"didnt find dicefinish tx\n");
6ca2e998 1087 }
1088 return(-1.);
7e988759 1089 }
1090 return(0.);
1091}
This page took 0.283789 seconds and 4 git commands to generate.