]> Git Repo - VerusCoin.git/blame - src/cc/dice.cpp
test
[VerusCoin.git] / src / cc / dice.cpp
CommitLineData
cfea7a46 1/******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
3 * *
4 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
5 * the top-level directory of this distribution for the individual copyright *
6 * holder information and the developer policies on copyright and licensing. *
7 * *
8 * Unless otherwise agreed in a custom licensing agreement, no part of the *
9 * SuperNET software, including this file may be copied, modified, propagated *
10 * or distributed except according to the terms contained in the LICENSE file *
11 * *
12 * Removal or modification of this copyright notice is prohibited. *
13 * *
14 ******************************************************************************/
15
16#include "CCdice.h"
cfea7a46 17
237dae63 18// hentropy_proof, timeout, validate, autoqueue, compare address to match house
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
43 vout.1: normal marker vout for easy searching
44 vout.2: normal change
45 vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
46
47addfunding (entropy):
48 vins.*: normal inputs
49 vout.0: CC vout for locked entropy funds
50 vout.1: tag to owner address for entropy funds
51 vout.2: normal change
52 vout.n-1: opreturn 'E' sbits fundingtxid hentropy
53
54bet:
55 vin.0: entropy txid from house
56 vins.1+: normal inputs
57 vout.0: CC vout for locked entropy
58 vout.1: CC vout for locked bet
59 vout.2: tag for bettor's address (txfee + odds)
60 vout.3: change
61 vout.n-1: opreturn 'B' sbits fundingtxid entropy
62
63winner:
64 vin.0: betTx CC vout.0 entropy from bet
65 vin.1: betTx CC vout.1 bet amount from bet
02409974 66 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 67 vout.0: funding CC change to entropy owner
68 vout.1: tag to owner address for entropy funds
69 vout.2: normal output to bettor's address
70 vout.n-1: opreturn 'W' sbits fundingtxid hentropy
71
72loser:
73 vin.0: betTx CC vout.0 entropy from bet
74 vin.1: betTx CC vout.1 bet amount from bet
02409974 75 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 76 vout.0: funding CC to entropy owner
77 vout.1: tag to owner address for entropy funds
78 vout.n-1: opreturn 'L' sbits fundingtxid hentropy
79
02409974 80timeout:
7eb60411 81 vin.0: betTx CC vout.0 entropy from bet
82 vin.1: betTx CC vout.1 bet amount from bet
02409974 83 vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 84 vout.0: funding CC vin.0 to entropy owner
85 vout.1: tag to owner address for entropy funds
86 vout.2: normal vin.1 to bettor's address
02409974 87 vout.n-1: opreturn 'T' sbits fundingtxid hentropy
7eb60411 88
89
10078e85 90 */
cfea7a46 91
ae748eaf 92#include "../endian.h"
93
94void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
95{
96 int32_t i,j=0;
97#if defined(WORDS_BIGENDIAN)
98 for (i=31; i>=0; i--)
99 dest[j++] = src[i];
100#else
17bc6201 101 memcpy(dest,src,len);
ae748eaf 102#endif
103}
104
dfe4c146 105uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
8138fad2 106{
ae748eaf 107 int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
72362bbe 108 memset(&hentropy,0,32);
e0e655fe 109 endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
d176d2df 110 txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
111 txidpub = curve25519(txidpriv,curve25519_basepoint9());
112
113 Myprivkey(tmp256.bytes);
114 vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
115 mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
116 mypub = curve25519(mypriv,curve25519_basepoint9());
117
91f21771 118 ssecret = curve25519(mypriv,txidpub);
119 ssecret2 = curve25519(txidpriv,mypub);
d176d2df 120 if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
8138fad2 121 {
ae748eaf 122 vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
123 vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
dfe4c146 124 endiancpy((uint8_t *)&entropy,_entropy,32);
125 endiancpy((uint8_t *)&hentropy,_hentropy,32);
840a96ca 126 }
127 else
128 {
129 for (i=0; i<32; i++)
774b8b44 130 fprintf(stderr,"%02x",ssecret.bytes[i]);
840a96ca 131 fprintf(stderr," ssecret\n");
132 for (i=0; i<32; i++)
ac9816bb 133 fprintf(stderr,"%02x",ssecret2.bytes[i]);
840a96ca 134 fprintf(stderr," ssecret2 dont match\n");
135 }
8138fad2 136 return(hentropy);
137}
138
9025093e 139uint64_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 140{
b52f7685 141 uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
98c6e2e1 142 if ( odds < 10000 )
10078e85 143 return(0);
98c6e2e1 144 else odds -= 10000;
be7a410a 145 if ( bet < minbet || bet > maxbet || odds > maxodds )
146 {
147 fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
148 return(0);
149 }
150 endiancpy(buf,(uint8_t *)&houseentropy,32);
151 endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
152 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
208f0e7a 153 endiancpy((uint8_t *)&house,_house,32);
be7a410a 154
155 endiancpy(buf,(uint8_t *)&bettorentropy,32);
156 endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
157 vcalc_sha256(0,(uint8_t *)&_house,buf,64);
208f0e7a 158 endiancpy((uint8_t *)&bettor,_bettor,32);
b52f7685 159 if ( odds > 1 )
160 bettor = (bettor / arith_uint256(odds));
161 if ( bettor >= house )
162 winnings = bet * odds;
163 else winnings = 0;
8a2eeb6b 164 fprintf(stderr,"bet %.8f at odds %d:1 %s vs %s\n",(double)bet/COIN,(int32_t)odds,uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
d0d96ab0 165 return(0);
10078e85 166}
167
9025093e 168CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
10078e85 169{
170 CScript opret; uint8_t evalcode = EVAL_DICE;
9025093e 171 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
10078e85 172 return(opret);
173}
174
9025093e 175uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
10078e85 176{
177 std::vector<uint8_t> vopret; uint8_t *script,e,f;
178 GetOpReturnData(scriptPubKey, vopret);
179 script = (uint8_t *)vopret.data();
9025093e 180 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 181 {
182 if ( e == EVAL_DICE && f == 'F' )
183 return(f);
184 }
185 return(0);
186}
187
afef48c0 188CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
10078e85 189{
190 CScript opret; uint8_t evalcode = EVAL_DICE;
afef48c0 191 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
10078e85 192 return(opret);
193}
194
54519f69 195uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
10078e85 196{
9025093e 197 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
753a6f92 198 GetOpReturnData(scriptPubKey,vopret);
10078e85 199 if ( vopret.size() > 2 )
200 {
201 script = (uint8_t *)vopret.data();
202 if ( script[0] == EVAL_DICE )
203 {
204 if ( script[1] == 'F' )
205 {
9025093e 206 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
10078e85 207 {
72362bbe 208 memset(&hash,0,32);
10078e85 209 fundingtxid = txid;
210 return('F');
211 } else fprintf(stderr,"unmarshal error for F\n");
212 }
54519f69 213 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
10078e85 214 {
02409974 215 if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
10078e85 216 return(f);
54519f69 217 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
10078e85 218 }
219 } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
220 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
221 return(0);
222}
cfea7a46 223
a03bb7fa 224uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
225{
54519f69 226 uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
227 if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
a03bb7fa 228 return(hash);
229 else return(zeroid);
230}
231
cfea7a46 232uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
233{
234 char destaddr[64];
235 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
236 {
237 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
238 return(tx.vout[v].nValue);
239 }
240 return(0);
241}
242
10078e85 243bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
cfea7a46 244{
245 static uint256 zerohash;
10078e85 246 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
cfea7a46 247 numvins = tx.vin.size();
248 numvouts = tx.vout.size();
249 for (i=0; i<numvins; i++)
250 {
cfea7a46 251 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
252 {
cfea7a46 253 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
10078e85 254 return eval->Invalid("always should find vin, but didnt");
cfea7a46 255 else
256 {
cfea7a46 257 if ( hashBlock == zerohash )
258 return eval->Invalid("cant dice from mempool");
259 if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
260 inputs += assetoshis;
261 }
262 }
263 }
264 for (i=0; i<numvouts; i++)
265 {
266 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
267 if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
268 outputs += assetoshis;
269 }
10078e85 270 if ( inputs != outputs+txfee )
cfea7a46 271 {
272 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
10078e85 273 return eval->Invalid("mismatched inputs != outputs + txfee");
cfea7a46 274 }
275 else return(true);
276}
277
1c87477b 278bool DiceIsmine(const CScript scriptPubKey)
279{
280 char destaddr[64],myaddr[64];
281 Getscriptaddress(destaddr,scriptPubKey);
ca05cbaa 282 Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
1c87477b 283 return(strcmp(destaddr,myaddr) == 0);
284}
285
7eb60411 286int32_t DiceIsWinner(int32_t mustbeme,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 287{
54519f69 288 uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2,entropy; uint8_t funcid;
753a6f92 289 //char str[65],str2[65];
7eb60411 290 if ( (mustbeme == 0 || DiceIsmine(vinTx.vout[1].scriptPubKey) != 0) && vinTx.vout.size() > 0 )
753a6f92 291 {
54519f69 292 if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') && sbits == vinsbits && fundingtxid == vinfundingtxid )
753a6f92 293 {
294 hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
295 if ( hentropy == hentropy2 )
296 {
297 winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
298 //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);
299 if ( winnings == 0 )
300 {
301 // queue 'L' losing tx
302 return(-1);
303 }
304 else
305 {
306 // queue 'W' winning tx
307 return(1);
308 }
309 }
310 }
311 }
312 return(0);
313}
314
cfea7a46 315bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
316{
54519f69 317 uint256 txid,fundingtxid,hashBlock,hash,proof; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
cfea7a46 318 numvins = tx.vin.size();
319 numvouts = tx.vout.size();
320 preventCCvins = preventCCvouts = -1;
321 if ( numvouts < 1 )
322 return eval->Invalid("no vouts");
323 else
324 {
10078e85 325 txid = tx.GetHash();
54519f69 326 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 327 {
10078e85 328 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
329 return eval->Invalid("cant find fundingtxid");
9025093e 330 else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
10078e85 331 return eval->Invalid("fundingTx not valid");
332 switch ( funcid )
cfea7a46 333 {
10078e85 334 case 'F':
335 //vins.*: normal inputs
336 //vout.0: CC vout for funding
337 //vout.1: normal marker vout for easy searching
338 //vout.2: normal change
7eb60411 339 //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
10078e85 340 return eval->Invalid("unexpected DiceValidate for createfunding");
341 break;
28b947b8 342 case 'E':
10078e85 343 //vins.*: normal inputs
0d4cfd0b 344 //vout.0: CC vout for locked entropy funds
345 //vout.1: tag to owner address for entropy funds
346 //vout.2: normal change
28b947b8 347 //vout.n-1: opreturn 'E' sbits fundingtxid hentropy
348 return eval->Invalid("unexpected DiceValidate for addfunding entropy");
10078e85 349 break;
04cf9f60 350 case 'B':
28b947b8 351 //vin.0: entropy txid from house
352 //vins.1+: normal inputs
0d4cfd0b 353 //vout.0: CC vout for locked entropy
28b947b8 354 //vout.1: CC vout for locked bet
98c6e2e1 355 //vout.2: tag for bettor's address (txfee + odds)
28b947b8 356 //vout.3: change
357 //vout.n-1: opreturn 'B' sbits fundingtxid entropy
1c87477b 358 if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
359 return eval->Invalid("always should find vin.0, but didnt");
7eb60411 360 if ( (iswin= DiceIsWinner(1,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
1c87477b 361 {
753a6f92 362 fprintf(stderr,"DiceIsWinner.%d\n",iswin);
1c87477b 363 }
e0e985ee 364 //return eval->Invalid("dont confirm bet during debug");
10078e85 365 break;
5d3d3a87 366 case 'L':
7eb60411 367 //vin.0: betTx CC vout.0 entropy from bet
368 //vin.1: betTx CC vout.1 bet amount from bet
02409974 369 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 370 //vout.0: funding CC to entropy owner
371 //vout.1: tag to owner address for entropy funds
372 //vout.n-1: opreturn 'L' sbits fundingtxid hentropy
5d3d3a87 373 break;
374 case 'W':
7eb60411 375 //vin.0: betTx CC vout.0 entropy from bet
376 //vin.1: betTx CC vout.1 bet amount from bet
02409974 377 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 378 //vout.0: funding CC change to entropy owner
379 //vout.1: tag to owner address for entropy funds
380 //vout.2: normal output to bettor's address
381 //vout.n-1: opreturn 'W' sbits fundingtxid hentropy
5d3d3a87 382 break;
02409974 383 case 'T':
7eb60411 384 //vin.0: betTx CC vout.0 entropy from bet
385 //vin.1: betTx CC vout.1 bet amount from bet
02409974 386 //vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
7eb60411 387 //vout.0: funding CC vin.0 to entropy owner
388 //vout.1: tag to owner address for entropy funds
389 //vout.2: normal vin.1 to bettor's address
02409974 390 //vout.n-1: opreturn 'T' sbits fundingtxid hentropy
7eb60411 391 /*for (i=0; i<numvins; i++)
10078e85 392 {
393 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
394 return eval->Invalid("unexpected normal vin for unlock");
395 }
396 if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
397 return false;
398 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
399 return eval->Invalid("always should find vin.0, but didnt");
400 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
401 return eval->Invalid("lock tx vout.0 is normal output");
402 else if ( tx.vout.size() < 3 )
403 return eval->Invalid("unlock tx not enough vouts");
404 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
405 return eval->Invalid("unlock tx vout.0 is normal output");
406 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
407 return eval->Invalid("unlock tx vout.1 is CC output");
408 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
409 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
410 amount = vinTx.vout[0].nValue;
98c6e2e1 411 reward = 0;
9025093e 412 //reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,timeoutblocks);
10078e85 413 if ( tx.vout[1].nValue > amount+reward )
414 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
7eb60411 415 preventCCvouts = 1;*/
10078e85 416 break;
cfea7a46 417 }
418 }
10078e85 419 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
cfea7a46 420 }
10078e85 421 return(true);
cfea7a46 422}
cfea7a46 423
10078e85 424uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
cfea7a46 425{
54519f69 426 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 427 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
428 GetCCaddress(cp,coinaddr,pk);
429 SetCCunspents(unspentOutputs,coinaddr);
430 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
431 {
432 txid = it->first.txhash;
10078e85 433 vout = (int32_t)it->first.index;
93ac96fa 434 if ( it->second.satoshis < 1000000 )
c2342f7b 435 continue;
10078e85 436 fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
437 for (j=0; j<mtx.vin.size(); j++)
438 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
439 break;
440 if ( j != mtx.vin.size() )
441 continue;
442 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
cfea7a46 443 {
54519f69 444 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
cfea7a46 445 {
10078e85 446 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
02409974 447 if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'W' && funcid != 'L' && funcid != 'T' )
10078e85 448 continue;
7eb60411 449 else if ( fundsflag == 0 && funcid != 'B' )
10078e85 450 continue;
cfea7a46 451 if ( total != 0 && maxinputs != 0 )
10078e85 452 {
453 if ( fundsflag == 0 )
454 scriptPubKey = tx.vout[1].scriptPubKey;
455 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
456 }
457 totalinputs += it->second.satoshis;
cfea7a46 458 n++;
459 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
460 break;
10078e85 461 } else fprintf(stderr,"null funcid\n");
cfea7a46 462 }
463 }
464 return(totalinputs);
465}
466
04cf9f60 467uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
10078e85 468{
54519f69 469 char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CTransaction tx; int32_t vout,first=0; uint8_t funcid;
10078e85 470 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
471 GetCCaddress(cp,coinaddr,pk);
472 SetCCunspents(unspentOutputs,coinaddr);
04cf9f60 473 entropyval = 0;
10078e85 474 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
475 {
476 txid = it->first.txhash;
477 vout = (int32_t)it->first.index;
478 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
479 {
dd042a0f 480 char str[65],str2[65];
54519f69 481 if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
10078e85 482 {
483 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
484 {
7eb60411 485 if ( funcid != 'B' && refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 )
04cf9f60 486 {
10078e85 487 totalinputs += nValue;
35d43add 488 fprintf(stderr,"add %.8f\n",(double)nValue/COIN);
02409974 489 if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
04cf9f60 490 {
491 entropytxid = txid;
492 entropyval = tx.vout[0].nValue;
493 first = 1;
494 }
495 }
10078e85 496 else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
76152f33 497 } else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
237dae63 498 } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
10078e85 499 }
500 }
501 return(totalinputs);
502}
503
a03bb7fa 504bool DicePlanExists(uint256 &fundingtxid,struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
10078e85 505{
506 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
507 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
508 GetCCaddress(cp,CCaddr,dicepk);
509 SetCCtxids(txids,CCaddr);
510 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
511 {
512 //int height = it->first.blockHeight;
513 txid = it->first.txhash;
a03bb7fa 514 if ( fundingtxid != zeroid && txid != fundingtxid )
515 continue;
10078e85 516 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
517 {
9025093e 518 if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
10078e85 519 {
520 if ( sbits == refsbits )
a03bb7fa 521 {
522 fundingtxid = txid;
10078e85 523 return(true);
a03bb7fa 524 }
10078e85 525 }
526 }
527 }
528 return(false);
529}
530
a03bb7fa 531struct CCcontract_info *Diceinit(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)
532{
39d9f276 533 struct CCcontract_info *cp; uint256 fundingtxid; int32_t cmpflag;
a03bb7fa 534 cp = CCinit(C,EVAL_DICE);
535 if ( txfee == 0 )
536 txfee = 10000;
537 mypk = pubkey2pk(Mypubkey());
538 dicepk = GetUnspendable(cp,0);
539 sbits = stringbits(planstr);
540 if ( reffundingtxid == zeroid )
541 cmpflag = 0;
542 else cmpflag = 1;
3cf03a67 543 fundingtxid = zeroid;
a03bb7fa 544 if ( DicePlanExists(fundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
545 {
546 fprintf(stderr,"Dice plan (%s) already exists\n",planstr);
547 return(0);
548 }
549 return(cp);
550}
551
10078e85 552UniValue DiceInfo(uint256 diceid)
553{
9025093e 554 UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits; char str[67],numstr[65];
10078e85 555 if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
556 {
557 fprintf(stderr,"cant find fundingtxid\n");
558 result.push_back(Pair("error","cant find fundingtxid"));
559 return(result);
560 }
9025093e 561 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
10078e85 562 {
563 fprintf(stderr,"fundingtxid isnt dice creation txid\n");
564 result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
565 return(result);
566 }
567 result.push_back(Pair("result","success"));
568 result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
569 unstringbits(str,sbits);
570 result.push_back(Pair("name",str));
571 result.push_back(Pair("sbits",sbits));
d0d96ab0 572 sprintf(numstr,"%.8f",(double)minbet/COIN);
573 result.push_back(Pair("minbet",numstr));
574 sprintf(numstr,"%.8f",(double)maxbet/COIN);
575 result.push_back(Pair("maxbet",numstr));
576 result.push_back(Pair("maxodds",maxodds));
9025093e 577 result.push_back(Pair("timeoutblocks",timeoutblocks));
10078e85 578 sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue/COIN);
579 result.push_back(Pair("funding",numstr));
580 return(result);
581}
582
583UniValue DiceList()
cfea7a46 584{
9025093e 585 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 586 cp = CCinit(&C,EVAL_DICE);
587 SetCCtxids(addressIndex,cp->normaladdr);
588 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
589 {
590 txid = it->first.txhash;
591 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
592 {
9025093e 593 if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
10078e85 594 {
595 result.push_back(uint256_str(str,txid));
596 }
597 }
598 }
599 return(result);
600}
601
9025093e 602std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
05177cfb 603{
a03bb7fa 604 CMutableTransaction mtx; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
9025093e 605 if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
05177cfb 606 {
607 fprintf(stderr,"negative parameter error\n");
608 return(0);
609 }
a03bb7fa 610 if ( (cp= Diceinit(zeroid,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
05177cfb 611 return(0);
10078e85 612 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
613 {
614 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
615 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
9025093e 616 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
10078e85 617 }
618 fprintf(stderr,"cant find enough inputs\n");
619 return(0);
620}
621
622std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
623{
a03bb7fa 624 CMutableTransaction mtx; uint256 entropy,hentropy; CPubKey mypk,dicepk; uint64_t sbits; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds,timeoutblocks;
10078e85 625 if ( amount < 0 )
626 {
627 fprintf(stderr,"negative parameter error\n");
628 return(0);
629 }
a03bb7fa 630 if ( (cp= Diceinit(fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
10078e85 631 return(0);
0d4cfd0b 632 if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 )
10078e85 633 {
8138fad2 634 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
10078e85 635 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
0d4cfd0b 636 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
afef48c0 637 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
10078e85 638 } else fprintf(stderr,"cant find enough inputs\n");
639 fprintf(stderr,"cant find fundingtxid\n");
cfea7a46 640 return(0);
641}
642
7347a801 643std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
5d3d3a87 644{
a03bb7fa 645 CMutableTransaction mtx; CPubKey mypk,dicepk; uint64_t sbits,entropyval; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropy,hentropy; struct CCcontract_info *cp,C;
7347a801 646 if ( bet < 0 || odds < 1 )
5d3d3a87 647 {
648 fprintf(stderr,"negative parameter error\n");
649 return(0);
650 }
a03bb7fa 651 if ( (cp= Diceinit(fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
7347a801 652 return(0);
653 if ( bet < minbet || bet > maxbet || odds > maxodds )
654 {
655 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);
656 return(0);
657 }
7eb60411 658 if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
7347a801 659 {
660 mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
661 if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
662 {
663 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
664 mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
665 mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
666 mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
afef48c0 667 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
7347a801 668 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
669 }
670 if ( entropyval == 0 && funding != 0 )
671 fprintf(stderr,"cant find dice entropy inputs\n");
672 else fprintf(stderr,"cant find dice inputs\n");
673 return(0);
674}
675
02409974 676std::string DiceWinLoseTimeout(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
7347a801 677{
afef48c0 678 CMutableTransaction mtx; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CScript scriptPubKey; CPubKey mypk,dicepk; struct CCcontract_info *cp,C; int64_t inputs,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid; int32_t iswin; uint64_t entropyval,sbits;
a03bb7fa 679 if ( (cp= Diceinit(fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
5d3d3a87 680 return(0);
a03bb7fa 681 if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
5d3d3a87 682 {
a03bb7fa 683 bettorentropy = DiceGetEntropy(betTx,'B');
afef48c0 684 // need to set hentropyproof
876bf0e5 685 if ( (iswin= DiceIsWinner(1,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
a03bb7fa 686 {
02409974 687 if ( iswin == winlosetimeout )
a03bb7fa 688 {
7eb60411 689 fprintf(stderr,"iswin.%d matches\n",iswin);
690 mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
691 mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
a03bb7fa 692 if ( iswin == 0 )
7eb60411 693 {
02409974 694 funcid = 'T';
695 fprintf(stderr,"timeout refunds are not supported yet\n");
7eb60411 696 }
a03bb7fa 697 else if ( iswin > 0 )
7eb60411 698 {
a03bb7fa 699 funcid = 'W';
7eb60411 700 odds = (betTx.vout[2].nValue - txfee);
701 if ( odds < 1 || odds > maxodds )
702 {
703 fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
704 return(0);
705 }
706 CCchange = betTx.vout[0].nValue;
707 fundsneeded = 2*txfee + (odds-1)*betTx.vout[1].nValue;
708 if ( (inputs= AddDiceInputs(scriptPubKey,1,cp,mtx,dicepk,fundsneeded,60)) > 0 )
709 {
710 if ( inputs > fundsneeded+txfee )
711 CCchange += (inputs - (fundsneeded+txfee));
712 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
713 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
714 mtx.vout.push_back(CTxOut(odds * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
715 }
716 else
717 {
718 fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
719 return(0);
720 }
721 }
722 else
a03bb7fa 723 {
7eb60411 724 funcid = 'L';
725 mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue - txfee,dicepk));
726 mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
727 }
728 hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
afef48c0 729 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
e0ea3b3e 730 } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
a03bb7fa 731 } else return(0);
732 }
5d3d3a87 733 return(0);
734}
735
This page took 0.162364 seconds and 4 git commands to generate.