]>
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 | |
18 | /* | |
10078e85 | 19 | in order to implement a dice game, we need a source of entropy, reasonably fast completion time and a way to manage the utxos. |
20 | ||
21 | 1. CC vout locks "house" funds with hash(entropy) + half of shared secret | |
22 | 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout. | |
23 | 3. house account sends funds to winner with proof of entropy | |
24 | 4. if timeout, bettor wins funds | |
25 | ||
26 | 2. and 3. can be done in mempool | |
27 | */ | |
cfea7a46 | 28 | |
ae748eaf | 29 | #include "../endian.h" |
30 | ||
31 | void endiancpy(uint8_t *dest,uint8_t *src,int32_t len) | |
32 | { | |
33 | int32_t i,j=0; | |
34 | #if defined(WORDS_BIGENDIAN) | |
35 | for (i=31; i>=0; i--) | |
36 | dest[j++] = src[i]; | |
37 | #else | |
17bc6201 | 38 | memcpy(dest,src,len); |
ae748eaf | 39 | #endif |
40 | } | |
41 | ||
dfe4c146 | 42 | uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used |
8138fad2 | 43 | { |
ae748eaf | 44 | int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy; |
72362bbe | 45 | memset(&hentropy,0,32); |
e0e655fe | 46 | endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32); |
d176d2df | 47 | txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40; |
48 | txidpub = curve25519(txidpriv,curve25519_basepoint9()); | |
49 | ||
50 | Myprivkey(tmp256.bytes); | |
51 | vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32); | |
52 | mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40; | |
53 | mypub = curve25519(mypriv,curve25519_basepoint9()); | |
54 | ||
91f21771 | 55 | ssecret = curve25519(mypriv,txidpub); |
56 | ssecret2 = curve25519(txidpriv,mypub); | |
d176d2df | 57 | if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 ) |
8138fad2 | 58 | { |
ae748eaf | 59 | vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32); |
60 | vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32); | |
dfe4c146 | 61 | endiancpy((uint8_t *)&entropy,_entropy,32); |
62 | endiancpy((uint8_t *)&hentropy,_hentropy,32); | |
840a96ca | 63 | } |
64 | else | |
65 | { | |
66 | for (i=0; i<32; i++) | |
774b8b44 | 67 | fprintf(stderr,"%02x",ssecret.bytes[i]); |
840a96ca | 68 | fprintf(stderr," ssecret\n"); |
69 | for (i=0; i<32; i++) | |
ac9816bb | 70 | fprintf(stderr,"%02x",ssecret2.bytes[i]); |
840a96ca | 71 | fprintf(stderr," ssecret2 dont match\n"); |
72 | } | |
8138fad2 | 73 | return(hentropy); |
74 | } | |
75 | ||
be7a410a | 76 | uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks,uint256 houseentropy,uint256 bettorentropy) |
10078e85 | 77 | { |
b52f7685 | 78 | uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65]; |
98c6e2e1 | 79 | if ( odds < 10000 ) |
10078e85 | 80 | return(0); |
98c6e2e1 | 81 | else odds -= 10000; |
be7a410a | 82 | if ( bet < minbet || bet > maxbet || odds > maxodds ) |
83 | { | |
84 | fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN); | |
85 | return(0); | |
86 | } | |
87 | endiancpy(buf,(uint8_t *)&houseentropy,32); | |
88 | endiancpy(&buf[32],(uint8_t *)&bettorentropy,32); | |
89 | vcalc_sha256(0,(uint8_t *)&_house,buf,64); | |
208f0e7a | 90 | endiancpy((uint8_t *)&house,_house,32); |
be7a410a | 91 | |
92 | endiancpy(buf,(uint8_t *)&bettorentropy,32); | |
93 | endiancpy(&buf[32],(uint8_t *)&houseentropy,32); | |
94 | vcalc_sha256(0,(uint8_t *)&_house,buf,64); | |
208f0e7a | 95 | endiancpy((uint8_t *)&bettor,_bettor,32); |
b52f7685 | 96 | if ( odds > 1 ) |
97 | bettor = (bettor / arith_uint256(odds)); | |
98 | if ( bettor >= house ) | |
99 | winnings = bet * odds; | |
100 | else winnings = 0; | |
8a2eeb6b | 101 | 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 | 102 | return(0); |
10078e85 | 103 | } |
104 | ||
d0d96ab0 | 105 | CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks) |
10078e85 | 106 | { |
107 | CScript opret; uint8_t evalcode = EVAL_DICE; | |
d0d96ab0 | 108 | opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << forfeitblocks); |
10078e85 | 109 | return(opret); |
110 | } | |
111 | ||
d0d96ab0 | 112 | uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &forfeitblocks) |
10078e85 | 113 | { |
114 | std::vector<uint8_t> vopret; uint8_t *script,e,f; | |
115 | GetOpReturnData(scriptPubKey, vopret); | |
116 | script = (uint8_t *)vopret.data(); | |
d0d96ab0 | 117 | if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> forfeitblocks) != 0 ) |
10078e85 | 118 | { |
119 | if ( e == EVAL_DICE && f == 'F' ) | |
120 | return(f); | |
121 | } | |
122 | return(0); | |
123 | } | |
124 | ||
8138fad2 | 125 | CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash) |
10078e85 | 126 | { |
127 | CScript opret; uint8_t evalcode = EVAL_DICE; | |
8138fad2 | 128 | opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash); |
10078e85 | 129 | return(opret); |
130 | } | |
131 | ||
8138fad2 | 132 | uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash) |
10078e85 | 133 | { |
d0d96ab0 | 134 | std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,forfeitblocks; |
10078e85 | 135 | GetOpReturnData(scriptPubKey, vopret); |
136 | if ( vopret.size() > 2 ) | |
137 | { | |
138 | script = (uint8_t *)vopret.data(); | |
139 | if ( script[0] == EVAL_DICE ) | |
140 | { | |
141 | if ( script[1] == 'F' ) | |
142 | { | |
d0d96ab0 | 143 | if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> forfeitblocks) != 0 ) |
10078e85 | 144 | { |
72362bbe | 145 | memset(&hash,0,32); |
10078e85 | 146 | fundingtxid = txid; |
147 | return('F'); | |
148 | } else fprintf(stderr,"unmarshal error for F\n"); | |
149 | } | |
8138fad2 | 150 | else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash) != 0 ) |
10078e85 | 151 | { |
28b947b8 | 152 | if ( e == EVAL_DICE && (f == 'B' || f == 'U' || f == 'E') ) |
10078e85 | 153 | return(f); |
154 | else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f); | |
155 | } | |
156 | } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]); | |
157 | } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); | |
158 | return(0); | |
159 | } | |
cfea7a46 | 160 | |
161 | uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) | |
162 | { | |
163 | char destaddr[64]; | |
164 | if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) | |
165 | { | |
166 | if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) | |
167 | return(tx.vout[v].nValue); | |
168 | } | |
169 | return(0); | |
170 | } | |
171 | ||
10078e85 | 172 | bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee) |
cfea7a46 | 173 | { |
174 | static uint256 zerohash; | |
10078e85 | 175 | CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis; |
cfea7a46 | 176 | numvins = tx.vin.size(); |
177 | numvouts = tx.vout.size(); | |
178 | for (i=0; i<numvins; i++) | |
179 | { | |
cfea7a46 | 180 | if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
181 | { | |
cfea7a46 | 182 | if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
10078e85 | 183 | return eval->Invalid("always should find vin, but didnt"); |
cfea7a46 | 184 | else |
185 | { | |
cfea7a46 | 186 | if ( hashBlock == zerohash ) |
187 | return eval->Invalid("cant dice from mempool"); | |
188 | if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) | |
189 | inputs += assetoshis; | |
190 | } | |
191 | } | |
192 | } | |
193 | for (i=0; i<numvouts; i++) | |
194 | { | |
195 | //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts); | |
196 | if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 ) | |
197 | outputs += assetoshis; | |
198 | } | |
10078e85 | 199 | if ( inputs != outputs+txfee ) |
cfea7a46 | 200 | { |
201 | fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); | |
10078e85 | 202 | return eval->Invalid("mismatched inputs != outputs + txfee"); |
cfea7a46 | 203 | } |
204 | else return(true); | |
205 | } | |
206 | ||
1c87477b | 207 | bool DiceIsmine(const CScript scriptPubKey) |
208 | { | |
209 | char destaddr[64],myaddr[64]; | |
210 | Getscriptaddress(destaddr,scriptPubKey); | |
ca05cbaa | 211 | Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG); |
1c87477b | 212 | return(strcmp(destaddr,myaddr) == 0); |
213 | } | |
214 | ||
cfea7a46 | 215 | bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) |
216 | { | |
72362bbe | 217 | uint256 txid,fundingtxid,hashBlock,hash; int64_t minbet,maxbet,maxodds,forfeitblocks; uint64_t sbits,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx; |
cfea7a46 | 218 | numvins = tx.vin.size(); |
219 | numvouts = tx.vout.size(); | |
220 | preventCCvins = preventCCvouts = -1; | |
221 | if ( numvouts < 1 ) | |
222 | return eval->Invalid("no vouts"); | |
223 | else | |
224 | { | |
10078e85 | 225 | txid = tx.GetHash(); |
72362bbe | 226 | if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash)) != 0 ) |
cfea7a46 | 227 | { |
10078e85 | 228 | if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 ) |
229 | return eval->Invalid("cant find fundingtxid"); | |
d0d96ab0 | 230 | else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,forfeitblocks) != 'F' ) |
10078e85 | 231 | return eval->Invalid("fundingTx not valid"); |
232 | switch ( funcid ) | |
cfea7a46 | 233 | { |
10078e85 | 234 | case 'F': |
235 | //vins.*: normal inputs | |
236 | //vout.0: CC vout for funding | |
237 | //vout.1: normal marker vout for easy searching | |
238 | //vout.2: normal change | |
239 | //vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit | |
240 | return eval->Invalid("unexpected DiceValidate for createfunding"); | |
241 | break; | |
28b947b8 | 242 | case 'E': |
10078e85 | 243 | //vins.*: normal inputs |
0d4cfd0b | 244 | //vout.0: CC vout for locked entropy funds |
245 | //vout.1: tag to owner address for entropy funds | |
246 | //vout.2: normal change | |
28b947b8 | 247 | //vout.n-1: opreturn 'E' sbits fundingtxid hentropy |
248 | return eval->Invalid("unexpected DiceValidate for addfunding entropy"); | |
10078e85 | 249 | break; |
04cf9f60 | 250 | case 'B': |
28b947b8 | 251 | //vin.0: entropy txid from house |
252 | //vins.1+: normal inputs | |
0d4cfd0b | 253 | //vout.0: CC vout for locked entropy |
28b947b8 | 254 | //vout.1: CC vout for locked bet |
98c6e2e1 | 255 | //vout.2: tag for bettor's address (txfee + odds) |
28b947b8 | 256 | //vout.3: change |
257 | //vout.n-1: opreturn 'B' sbits fundingtxid entropy | |
258 | // get house hentropy and its vin0.prevtxid, cmp vout1 to owner address | |
259 | // if owneraddress is me, then validate hentropy and submit outcome tx | |
260 | fprintf(stderr,"got bet txid\n"); | |
1c87477b | 261 | if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) |
262 | return eval->Invalid("always should find vin.0, but didnt"); | |
69d725fe | 263 | else if ( DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout.size() > 0 ) |
1c87477b | 264 | { |
77add0c1 | 265 | uint64_t vinsbits,winnings; uint256 vinfundingtxid,hentropy,hentropy2,entropy; char str[65],str2[65]; |
da297d05 | 266 | if ( DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy) == 'E' && sbits == vinsbits && fundingtxid == vinfundingtxid ) |
69d725fe | 267 | { |
93235ec9 | 268 | hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash); |
a806f2fc | 269 | if ( hentropy == hentropy2 ) |
270 | { | |
98c6e2e1 | 271 | winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,forfeitblocks,entropy,hash); |
be7a410a | 272 | 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); |
a806f2fc | 273 | } |
69d725fe | 274 | } |
1c87477b | 275 | } |
28b947b8 | 276 | return eval->Invalid("dont confirm bet during debug"); |
10078e85 | 277 | break; |
278 | case 'U': | |
279 | //vin.0: locked funds CC vout.0 from lock | |
28b947b8 | 280 | //vin.1+: funding CC vout.0 from 'F' and 'E' and 'U' |
10078e85 | 281 | //vout.0: funding CC change |
282 | //vout.1: normal output to unlock address | |
283 | //vout.n-1: opreturn 'U' sbits fundingtxid | |
284 | for (i=0; i<numvins; i++) | |
285 | { | |
286 | if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 ) | |
287 | return eval->Invalid("unexpected normal vin for unlock"); | |
288 | } | |
289 | if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 ) | |
290 | return false; | |
291 | else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) | |
292 | return eval->Invalid("always should find vin.0, but didnt"); | |
293 | else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) | |
294 | return eval->Invalid("lock tx vout.0 is normal output"); | |
295 | else if ( tx.vout.size() < 3 ) | |
296 | return eval->Invalid("unlock tx not enough vouts"); | |
297 | else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) | |
298 | return eval->Invalid("unlock tx vout.0 is normal output"); | |
299 | else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 ) | |
300 | return eval->Invalid("unlock tx vout.1 is CC output"); | |
301 | else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey ) | |
302 | return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey"); | |
303 | amount = vinTx.vout[0].nValue; | |
98c6e2e1 | 304 | reward = 0; |
305 | //reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,forfeitblocks); | |
10078e85 | 306 | if ( tx.vout[1].nValue > amount+reward ) |
307 | return eval->Invalid("unlock tx vout.1 isnt amount+reward"); | |
308 | preventCCvouts = 1; | |
309 | break; | |
cfea7a46 | 310 | } |
311 | } | |
10078e85 | 312 | return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); |
cfea7a46 | 313 | } |
10078e85 | 314 | return(true); |
cfea7a46 | 315 | } |
cfea7a46 | 316 | |
28b947b8 | 317 | // 'B' vs 'F' and 'E' |
10078e85 | 318 | uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs) |
cfea7a46 | 319 | { |
72362bbe | 320 | char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hash,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; |
cfea7a46 | 321 | std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
322 | GetCCaddress(cp,coinaddr,pk); | |
323 | SetCCunspents(unspentOutputs,coinaddr); | |
324 | for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) | |
325 | { | |
326 | txid = it->first.txhash; | |
10078e85 | 327 | vout = (int32_t)it->first.index; |
328 | fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); | |
329 | for (j=0; j<mtx.vin.size(); j++) | |
330 | if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n ) | |
331 | break; | |
332 | if ( j != mtx.vin.size() ) | |
333 | continue; | |
334 | if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) | |
cfea7a46 | 335 | { |
72362bbe | 336 | if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash)) != 0 ) |
cfea7a46 | 337 | { |
10078e85 | 338 | fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN); |
28b947b8 | 339 | if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'U' ) |
10078e85 | 340 | continue; |
04cf9f60 | 341 | else if ( fundsflag == 0 && (funcid != 'B' || tx.vout.size() < 4) ) |
10078e85 | 342 | continue; |
cfea7a46 | 343 | if ( total != 0 && maxinputs != 0 ) |
10078e85 | 344 | { |
345 | if ( fundsflag == 0 ) | |
346 | scriptPubKey = tx.vout[1].scriptPubKey; | |
347 | mtx.vin.push_back(CTxIn(txid,vout,CScript())); | |
348 | } | |
349 | totalinputs += it->second.satoshis; | |
cfea7a46 | 350 | n++; |
351 | if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) | |
352 | break; | |
10078e85 | 353 | } else fprintf(stderr,"null funcid\n"); |
cfea7a46 | 354 | } |
355 | } | |
356 | return(totalinputs); | |
357 | } | |
358 | ||
04cf9f60 | 359 | uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid) |
10078e85 | 360 | { |
04cf9f60 | 361 | char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 hash,txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout,first=0; uint8_t funcid; |
10078e85 | 362 | std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
363 | GetCCaddress(cp,coinaddr,pk); | |
364 | SetCCunspents(unspentOutputs,coinaddr); | |
04cf9f60 | 365 | entropyval = 0; |
10078e85 | 366 | for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
367 | { | |
368 | txid = it->first.txhash; | |
369 | vout = (int32_t)it->first.index; | |
370 | if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) | |
371 | { | |
72362bbe | 372 | if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash)) != 0 ) |
10078e85 | 373 | { |
374 | if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) | |
375 | { | |
376 | if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 ) | |
04cf9f60 | 377 | { |
10078e85 | 378 | totalinputs += nValue; |
35d43add | 379 | fprintf(stderr,"add %.8f\n",(double)nValue/COIN); |
28b947b8 | 380 | if ( first == 0 && funcid == 'E' ) |
04cf9f60 | 381 | { |
382 | entropytxid = txid; | |
383 | entropyval = tx.vout[0].nValue; | |
384 | first = 1; | |
385 | } | |
386 | } | |
10078e85 | 387 | else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN); |
35d43add | 388 | } else fprintf(stderr,"else case funcid %d\n",funcid); |
10078e85 | 389 | } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN); |
390 | } | |
391 | } | |
392 | return(totalinputs); | |
393 | } | |
394 | ||
d0d96ab0 | 395 | bool DicePlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &forfeitblocks) |
10078e85 | 396 | { |
397 | char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx; | |
398 | std::vector<std::pair<CAddressIndexKey, CAmount> > txids; | |
399 | GetCCaddress(cp,CCaddr,dicepk); | |
400 | SetCCtxids(txids,CCaddr); | |
401 | for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++) | |
402 | { | |
403 | //int height = it->first.blockHeight; | |
404 | txid = it->first.txhash; | |
405 | if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) | |
406 | { | |
d0d96ab0 | 407 | if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,forfeitblocks) == 'F' ) |
10078e85 | 408 | { |
409 | if ( sbits == refsbits ) | |
410 | return(true); | |
411 | } | |
412 | } | |
413 | } | |
414 | return(false); | |
415 | } | |
416 | ||
417 | UniValue DiceInfo(uint256 diceid) | |
418 | { | |
d0d96ab0 | 419 | UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; int64_t minbet,maxbet,maxodds,forfeitblocks; uint64_t sbits; char str[67],numstr[65]; |
10078e85 | 420 | if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 ) |
421 | { | |
422 | fprintf(stderr,"cant find fundingtxid\n"); | |
423 | result.push_back(Pair("error","cant find fundingtxid")); | |
424 | return(result); | |
425 | } | |
d0d96ab0 | 426 | if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,forfeitblocks) == 0 ) |
10078e85 | 427 | { |
428 | fprintf(stderr,"fundingtxid isnt dice creation txid\n"); | |
429 | result.push_back(Pair("error","fundingtxid isnt dice creation txid")); | |
430 | return(result); | |
431 | } | |
432 | result.push_back(Pair("result","success")); | |
433 | result.push_back(Pair("fundingtxid",uint256_str(str,diceid))); | |
434 | unstringbits(str,sbits); | |
435 | result.push_back(Pair("name",str)); | |
436 | result.push_back(Pair("sbits",sbits)); | |
d0d96ab0 | 437 | sprintf(numstr,"%.8f",(double)minbet/COIN); |
438 | result.push_back(Pair("minbet",numstr)); | |
439 | sprintf(numstr,"%.8f",(double)maxbet/COIN); | |
440 | result.push_back(Pair("maxbet",numstr)); | |
441 | result.push_back(Pair("maxodds",maxodds)); | |
442 | result.push_back(Pair("forfeitblocks",forfeitblocks)); | |
10078e85 | 443 | sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue/COIN); |
444 | result.push_back(Pair("funding",numstr)); | |
445 | return(result); | |
446 | } | |
447 | ||
448 | UniValue DiceList() | |
cfea7a46 | 449 | { |
d0d96ab0 | 450 | 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,forfeitblocks; char str[65]; |
10078e85 | 451 | cp = CCinit(&C,EVAL_DICE); |
452 | SetCCtxids(addressIndex,cp->normaladdr); | |
453 | for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) | |
454 | { | |
455 | txid = it->first.txhash; | |
456 | if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) | |
457 | { | |
d0d96ab0 | 458 | if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,forfeitblocks) != 0 ) |
10078e85 | 459 | { |
460 | result.push_back(uint256_str(str,txid)); | |
461 | } | |
462 | } | |
463 | } | |
464 | return(result); | |
465 | } | |
466 | ||
d0d96ab0 | 467 | std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks) |
10078e85 | 468 | { |
d0d96ab0 | 469 | CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t sbits; int64_t a,b,c,d; struct CCcontract_info *cp,C; |
0e84bf92 | 470 | if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || forfeitblocks < 0 || forfeitblocks > 1440 ) |
10078e85 | 471 | { |
472 | fprintf(stderr,"negative parameter error\n"); | |
473 | return(0); | |
474 | } | |
cfea7a46 | 475 | cp = CCinit(&C,EVAL_DICE); |
476 | if ( txfee == 0 ) | |
477 | txfee = 10000; | |
10078e85 | 478 | mypk = pubkey2pk(Mypubkey()); |
cfea7a46 | 479 | dicepk = GetUnspendable(cp,0); |
10078e85 | 480 | sbits = stringbits(planstr); |
481 | if ( DicePlanExists(cp,sbits,dicepk,a,b,c,d) != 0 ) | |
482 | { | |
483 | fprintf(stderr,"Dice plan (%s) already exists\n",planstr); | |
484 | return(0); | |
485 | } | |
486 | if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 ) | |
487 | { | |
488 | mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk)); | |
489 | mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG)); | |
d0d96ab0 | 490 | return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,forfeitblocks))); |
10078e85 | 491 | } |
492 | fprintf(stderr,"cant find enough inputs\n"); | |
493 | return(0); | |
494 | } | |
495 | ||
496 | std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount) | |
497 | { | |
8138fad2 | 498 | CMutableTransaction mtx; uint256 entropy,hentropy; CPubKey mypk,dicepk; CScript opret; uint64_t sbits; int64_t a,b,c,d; struct CCcontract_info *cp,C; |
10078e85 | 499 | if ( amount < 0 ) |
500 | { | |
501 | fprintf(stderr,"negative parameter error\n"); | |
502 | return(0); | |
503 | } | |
504 | cp = CCinit(&C,EVAL_DICE); | |
505 | if ( txfee == 0 ) | |
506 | txfee = 10000; | |
cfea7a46 | 507 | mypk = pubkey2pk(Mypubkey()); |
10078e85 | 508 | dicepk = GetUnspendable(cp,0); |
509 | sbits = stringbits(planstr); | |
510 | if ( DicePlanExists(cp,sbits,dicepk,a,b,c,d) == 0 ) | |
511 | { | |
512 | fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); | |
513 | return(0); | |
514 | } | |
515 | sbits = stringbits(planstr); | |
0d4cfd0b | 516 | if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 ) |
10078e85 | 517 | { |
8138fad2 | 518 | hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); |
10078e85 | 519 | mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk)); |
0d4cfd0b | 520 | mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
28b947b8 | 521 | return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy))); |
10078e85 | 522 | } else fprintf(stderr,"cant find enough inputs\n"); |
523 | fprintf(stderr,"cant find fundingtxid\n"); | |
cfea7a46 | 524 | return(0); |
525 | } | |
526 | ||
d0d96ab0 | 527 | std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds) |
cfea7a46 | 528 | { |
04cf9f60 | 529 | CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t sbits,entropyval; int64_t funding,minbet,maxbet,maxodds,forfeitblocks; uint256 entropytxid,entropy,hentropy; struct CCcontract_info *cp,C; |
c857567a | 530 | if ( bet < 0 || odds < 1 ) |
10078e85 | 531 | { |
532 | fprintf(stderr,"negative parameter error\n"); | |
533 | return(0); | |
534 | } | |
cfea7a46 | 535 | cp = CCinit(&C,EVAL_DICE); |
536 | if ( txfee == 0 ) | |
537 | txfee = 10000; | |
538 | mypk = pubkey2pk(Mypubkey()); | |
539 | dicepk = GetUnspendable(cp,0); | |
10078e85 | 540 | sbits = stringbits(planstr); |
d0d96ab0 | 541 | if ( DicePlanExists(cp,sbits,dicepk,minbet,maxbet,maxodds,forfeitblocks) == 0 ) |
cfea7a46 | 542 | { |
10078e85 | 543 | fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); |
544 | return(0); | |
545 | } | |
be7a410a | 546 | if ( bet < minbet || bet > maxbet || odds > maxodds ) |
10078e85 | 547 | { |
be7a410a | 548 | 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); |
10078e85 | 549 | return(0); |
550 | } | |
04cf9f60 | 551 | if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= bet*odds+txfee && entropyval != 0 ) |
10078e85 | 552 | { |
0d4cfd0b | 553 | mtx.vin.push_back(CTxIn(entropytxid,0,CScript())); |
98c6e2e1 | 554 | if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 ) |
10078e85 | 555 | { |
8138fad2 | 556 | hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); |
04cf9f60 | 557 | mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk)); |
3f9f8229 | 558 | mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk)); |
98c6e2e1 | 559 | mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); |
28b947b8 | 560 | return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy))); |
3f9f8229 | 561 | } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN); |
cfea7a46 | 562 | } |
04cf9f60 | 563 | if ( entropyval == 0 && funding != 0 ) |
564 | fprintf(stderr,"cant find dice entropy inputs\n"); | |
565 | else fprintf(stderr,"cant find dice inputs\n"); | |
cfea7a46 | 566 | return(0); |
567 | } | |
568 | ||
10078e85 | 569 | std::string DiceUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid) |
570 | { | |
5db44aff | 571 | int32_t houseflag = 1; |
8138fad2 | 572 | CMutableTransaction mtx; CTransaction tx; char coinaddr[64]; CPubKey mypk,dicepk; CScript opret,scriptPubKey,ignore; uint256 hashBlock,entropy,hentropy; uint64_t funding,sbits,reward=0,amount=0,inputs,CCchange=0; int64_t minbet,maxbet,maxodds,forfeitblocks; struct CCcontract_info *cp,C; |
10078e85 | 573 | cp = CCinit(&C,EVAL_DICE); |
574 | if ( txfee == 0 ) | |
575 | txfee = 10000; | |
576 | dicepk = GetUnspendable(cp,0); | |
577 | mypk = pubkey2pk(Mypubkey()); | |
578 | sbits = stringbits(planstr); | |
d0d96ab0 | 579 | if ( DicePlanExists(cp,sbits,dicepk,minbet,maxbet,maxodds,forfeitblocks) == 0 ) |
10078e85 | 580 | { |
581 | fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); | |
582 | return(0); | |
583 | } | |
584 | // need to deal with finding the right utxos | |
585 | if ( locktxid == zeroid ) | |
586 | amount = AddDiceInputs(scriptPubKey,0,cp,mtx,dicepk,(1LL << 30),1); | |
587 | else | |
588 | { | |
589 | GetCCaddress(cp,coinaddr,dicepk); | |
590 | if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 ) | |
591 | { | |
592 | fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr); | |
593 | return(0); | |
594 | } | |
595 | if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) | |
596 | { | |
597 | scriptPubKey = tx.vout[1].scriptPubKey; | |
598 | mtx.vin.push_back(CTxIn(locktxid,0,CScript())); | |
599 | } | |
600 | else | |
601 | { | |
602 | fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr); | |
603 | return(0); | |
604 | } | |
605 | } | |
98c6e2e1 | 606 | reward = 0;//DiceCalc(amount,mtx.vin[0].prevout.hash,minbet,maxbet,maxodds,forfeitblocks); |
607 | if ( amount > 0 && reward > txfee && scriptPubKey.size() > 0 ) | |
10078e85 | 608 | { |
609 | if ( (inputs= AddDiceInputs(ignore,1,cp,mtx,dicepk,reward+txfee,30)) > 0 ) | |
610 | { | |
611 | if ( inputs >= (reward + 2*txfee) ) | |
612 | CCchange = (inputs - (reward + txfee)); | |
613 | fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN); | |
8138fad2 | 614 | if ( houseflag != 0 ) |
615 | hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); | |
616 | else hentropy = zeroid; | |
10078e85 | 617 | mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk)); |
618 | mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey)); | |
8138fad2 | 619 | return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeDiceOpRet('U',sbits,fundingtxid,hentropy))); |
10078e85 | 620 | } |
621 | fprintf(stderr,"cant find enough dice inputs\n"); | |
622 | } | |
623 | fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN); | |
624 | return(0); | |
625 | } | |
cfea7a46 | 626 |