]>
Commit | Line | Data |
---|---|---|
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 | ||
40 | createfunding: | |
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 | ||
48 | addfunding (entropy): | |
49 | vins.*: normal inputs | |
50 | vout.0: CC vout for locked entropy funds | |
51 | vout.1: tag to owner address for entropy funds | |
52 | vout.2: normal change | |
53 | vout.n-1: opreturn 'E' sbits fundingtxid hentropy | |
54 | ||
55 | bet: | |
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 | 64 | loser: |
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 | ||
74 | winner: | |
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 | 79 | timeout: |
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 | 86 | static uint256 bettxids[128]; |
7377f6ea | 87 | |
88 | struct dicefinish_info | |
89 | { | |
e787eaee | 90 | uint256 fundingtxid,bettxid; |
7377f6ea | 91 | uint64_t sbits; |
92 | int32_t iswin; | |
93 | }; | |
94 | ||
b3c09a21 | 95 | bool 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 | 115 | void *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 | 149 | void 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 | 162 | void endiancpy(uint8_t *dest,uint8_t *src,int32_t len) |
163 | { | |
164 | int32_t i,j=0; | |
165 | #if defined(WORDS_BIGENDIAN) | |
166 | for (i=31; i>=0; i--) | |
167 | dest[j++] = src[i]; | |
168 | #else | |
17bc6201 | 169 | memcpy(dest,src,len); |
ae748eaf | 170 | #endif |
171 | } | |
172 | ||
00005381 | 173 | CPubKey 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 | 186 | uint256 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 | 222 | uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy) |
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 | 252 | CScript 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 | 259 | uint8_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 | 272 | CScript 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 | 279 | uint8_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 | 308 | uint256 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 | 316 | uint64_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 | 330 | int64_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 | 358 | bool 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 | 366 | int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction vinTx,uint256 bettorentropy,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 fundingtxid) |
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 | 396 | bool 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 | ||
404 | bool 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 | 574 | uint64_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 | 617 | int64_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 | 697 | bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks) |
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 | 737 | struct CCcontract_info *Diceinit(CScript &fundingPubKey,uint256 reffundingtxid,struct CCcontract_info *C,char *planstr,uint64_t &txfee,CPubKey &mypk,CPubKey &dicepk,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks) |
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 | 757 | UniValue 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 | ||
791 | UniValue 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 | 810 | std::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 | ||
832 | std::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 | 868 | std::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 | 906 | std::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 | 1015 | double 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 | } |