844 lines
39 KiB
C++
844 lines
39 KiB
C++
/******************************************************************************
|
|
* Copyright © 2014-2018 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
#include "CCdice.h"
|
|
|
|
// timeout, validate
|
|
|
|
/*
|
|
in order to implement a dice game, we need a source of entropy, reasonably fast completion time and a way to manage the utxos.
|
|
|
|
1. CC vout locks "house" funds with hash(entropy)
|
|
2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout.
|
|
3. house account sends funds to winner/loser with proof of entropy
|
|
4. if timeout, bettor gets refund
|
|
|
|
2. and 3. can be done in mempool
|
|
|
|
The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
createfunding:
|
|
vins.*: normal inputs
|
|
vout.0: CC vout for funding
|
|
vout.1: owner vout
|
|
vout.2: dice marker address vout for easy searching
|
|
vout.3: normal change
|
|
vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
|
|
|
|
addfunding (entropy):
|
|
vins.*: normal inputs
|
|
vout.0: CC vout for locked entropy funds
|
|
vout.1: tag to owner address for entropy funds
|
|
vout.2: normal change
|
|
vout.n-1: opreturn 'E' sbits fundingtxid hentropy
|
|
|
|
bet:
|
|
vin.0: entropy txid from house
|
|
vins.1+: normal inputs
|
|
vout.0: CC vout for locked entropy
|
|
vout.1: CC vout for locked bet
|
|
vout.2: tag for bettor's address (txfee + odds)
|
|
vout.3: change
|
|
vout.n-1: opreturn 'B' sbits fundingtxid entropy
|
|
|
|
winner:
|
|
vin.0: betTx CC vout.0 entropy from bet
|
|
vin.1: betTx CC vout.1 bet amount from bet
|
|
vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
vout.0: funding CC change to entropy owner
|
|
vout.1: tag to owner address for entropy funds
|
|
vout.2: normal output to bettor's address
|
|
vout.n-1: opreturn 'W' sbits fundingtxid hentropy
|
|
|
|
loser:
|
|
vin.0: betTx CC vout.0 entropy from bet
|
|
vin.1: betTx CC vout.1 bet amount from bet
|
|
vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
vout.0: funding CC to entropy owner
|
|
vout.1: tag to owner address for entropy funds
|
|
vout.n-1: opreturn 'L' sbits fundingtxid hentropy
|
|
|
|
timeout:
|
|
vin.0: betTx CC vout.0 entropy from bet
|
|
vin.1: betTx CC vout.1 bet amount from bet
|
|
vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
vout.0: funding CC vin.0 to entropy owner
|
|
vout.1: tag to owner address for entropy funds
|
|
vout.2: normal vin.1 to bettor's address
|
|
vout.n-1: opreturn 'T' sbits fundingtxid hentropy
|
|
|
|
|
|
*/
|
|
|
|
#include "../endian.h"
|
|
|
|
static uint256 bettxids[128];
|
|
|
|
struct dicefinish_info
|
|
{
|
|
uint256 fundingtxid,bettxid;
|
|
uint64_t sbits;
|
|
int32_t iswin;
|
|
};
|
|
|
|
void *dicefinish(void *_ptr)
|
|
{
|
|
char str[65],str2[65],name[32]; std::string res; int32_t i,duplicate=0; struct dicefinish_info *ptr;
|
|
ptr = (struct dicefinish_info *)_ptr;
|
|
sleep(1);
|
|
for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
|
|
if ( bettxids[i] == ptr->bettxid )
|
|
{
|
|
duplicate = 1;
|
|
break;
|
|
}
|
|
if ( duplicate == 0 )
|
|
{
|
|
for (i=0; i<sizeof(bettxids)/sizeof(*bettxids); i++)
|
|
if ( bettxids[i] == zeroid )
|
|
{
|
|
bettxids[i] = ptr->bettxid;
|
|
break;
|
|
}
|
|
if ( i == sizeof(bettxids)/sizeof(*bettxids) )
|
|
bettxids[rand() % i] = ptr->bettxid;
|
|
}
|
|
unstringbits(name,ptr->sbits);
|
|
fprintf(stderr,"duplicate.%d dicefinish.%d %s funding.%s bid.%s\n",duplicate,ptr->iswin,name,uint256_str(str,ptr->fundingtxid),uint256_str(str2,ptr->bettxid));
|
|
if ( duplicate == 0 )
|
|
{
|
|
CTransaction tx; uint256 txid; char str[65]; int32_t result;
|
|
res = DiceWinLoseTimeout(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin);
|
|
if ( result != 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
|
|
{
|
|
LOCK(cs_main);
|
|
if ( DecodeHexTx(tx,res) != 0 )
|
|
{
|
|
txid = tx.GetHash();
|
|
RelayTransaction(tx);
|
|
fprintf(stderr,"%s\nresult.(%s)\n",res.c_str(),uint256_str(str,txid));
|
|
}
|
|
} else fprintf(stderr,"non-hex result.(%s)\n",res.c_str());
|
|
}
|
|
free(ptr);
|
|
return(0);
|
|
}
|
|
|
|
void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid)
|
|
{
|
|
struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
|
|
ptr->fundingtxid = fundingtxid;
|
|
ptr->bettxid = bettxid;
|
|
ptr->sbits = sbits;
|
|
ptr->iswin = iswin;
|
|
if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 )
|
|
{
|
|
fprintf(stderr,"DiceQueue.%d\n",iswin);
|
|
} // small memory leak per DiceQueue
|
|
}
|
|
|
|
void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
|
|
{
|
|
int32_t i,j=0;
|
|
#if defined(WORDS_BIGENDIAN)
|
|
for (i=31; i>=0; i--)
|
|
dest[j++] = src[i];
|
|
#else
|
|
memcpy(dest,src,len);
|
|
#endif
|
|
}
|
|
|
|
uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used
|
|
{
|
|
int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
|
|
memset(&hentropy,0,32);
|
|
endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
|
|
txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
|
|
txidpub = curve25519(txidpriv,curve25519_basepoint9());
|
|
|
|
Myprivkey(tmp256.bytes);
|
|
vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
|
|
mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
|
|
mypub = curve25519(mypriv,curve25519_basepoint9());
|
|
|
|
ssecret = curve25519(mypriv,txidpub);
|
|
ssecret2 = curve25519(txidpriv,mypub);
|
|
if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
|
|
{
|
|
vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
|
|
vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
|
|
endiancpy((uint8_t *)&entropy,_entropy,32);
|
|
endiancpy((uint8_t *)&hentropy,_hentropy,32);
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i<32; i++)
|
|
fprintf(stderr,"%02x",ssecret.bytes[i]);
|
|
fprintf(stderr," ssecret\n");
|
|
for (i=0; i<32; i++)
|
|
fprintf(stderr,"%02x",ssecret2.bytes[i]);
|
|
fprintf(stderr," ssecret2 dont match\n");
|
|
}
|
|
return(hentropy);
|
|
}
|
|
|
|
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)
|
|
{
|
|
uint8_t buf[64],_house[32],_bettor[32]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65];
|
|
if ( odds < 10000 )
|
|
return(0);
|
|
else odds -= 10000;
|
|
if ( bet < minbet || bet > maxbet || odds > maxodds )
|
|
{
|
|
fprintf(stderr,"bet size violation %.8f\n",(double)bet/COIN);
|
|
return(0);
|
|
}
|
|
endiancpy(buf,(uint8_t *)&houseentropy,32);
|
|
endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
|
|
vcalc_sha256(0,(uint8_t *)&_house,buf,64);
|
|
endiancpy((uint8_t *)&house,_house,32);
|
|
|
|
endiancpy(buf,(uint8_t *)&bettorentropy,32);
|
|
endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
|
|
vcalc_sha256(0,(uint8_t *)&_house,buf,64);
|
|
endiancpy((uint8_t *)&bettor,_bettor,32);
|
|
if ( odds > 1 )
|
|
bettor = (bettor / arith_uint256(odds));
|
|
if ( bettor >= house )
|
|
winnings = bet * odds;
|
|
else winnings = 0;
|
|
fprintf(stderr,"winnings %.8f bet %.8f at odds %d:1 %s vs %s\n",(double)winnings/COIN,(double)bet/COIN,(int32_t)odds,uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
|
|
return(winnings);
|
|
}
|
|
|
|
CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
|
|
{
|
|
CScript opret; uint8_t evalcode = EVAL_DICE;
|
|
opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
|
|
return(opret);
|
|
}
|
|
|
|
uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
|
|
{
|
|
std::vector<uint8_t> vopret; uint8_t *script,e,f;
|
|
GetOpReturnData(scriptPubKey, vopret);
|
|
script = (uint8_t *)vopret.data();
|
|
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
|
|
{
|
|
if ( e == EVAL_DICE && f == 'F' )
|
|
return(f);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
|
|
{
|
|
CScript opret; uint8_t evalcode = EVAL_DICE;
|
|
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
|
|
return(opret);
|
|
}
|
|
|
|
uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
|
|
{
|
|
std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
|
|
GetOpReturnData(scriptPubKey,vopret);
|
|
if ( vopret.size() > 2 )
|
|
{
|
|
script = (uint8_t *)vopret.data();
|
|
if ( script[0] == EVAL_DICE )
|
|
{
|
|
if ( script[1] == 'F' )
|
|
{
|
|
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
|
|
{
|
|
memset(&hash,0,32);
|
|
fundingtxid = txid;
|
|
return('F');
|
|
} else fprintf(stderr,"unmarshal error for F\n");
|
|
}
|
|
else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
|
|
{
|
|
if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
|
|
return(f);
|
|
else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
|
|
}
|
|
} else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
|
|
} else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
|
|
return(0);
|
|
}
|
|
|
|
uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
|
|
{
|
|
uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
|
|
if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
|
|
return(hash);
|
|
else return(zeroid);
|
|
}
|
|
|
|
uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
|
|
{
|
|
char destaddr[64];
|
|
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
|
{
|
|
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
|
return(tx.vout[v].nValue);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
|
|
{
|
|
static uint256 zerohash;
|
|
CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
|
|
numvins = tx.vin.size();
|
|
numvouts = tx.vout.size();
|
|
for (i=0; i<numvins; i++)
|
|
{
|
|
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
|
{
|
|
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
|
return eval->Invalid("always should find vin, but didnt");
|
|
else
|
|
{
|
|
if ( hashBlock == zerohash )
|
|
return eval->Invalid("cant dice from mempool");
|
|
if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
|
|
inputs += assetoshis;
|
|
}
|
|
}
|
|
}
|
|
for (i=0; i<numvouts; i++)
|
|
{
|
|
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|
if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 )
|
|
outputs += assetoshis;
|
|
}
|
|
if ( inputs != outputs+txfee )
|
|
{
|
|
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
|
return eval->Invalid("mismatched inputs != outputs + txfee");
|
|
}
|
|
else return(true);
|
|
}
|
|
|
|
bool DiceIsmine(const CScript scriptPubKey)
|
|
{
|
|
char destaddr[64],myaddr[64];
|
|
Getscriptaddress(destaddr,scriptPubKey);
|
|
Getscriptaddress(myaddr,CScript() << Mypubkey() << OP_CHECKSIG);
|
|
return(strcmp(destaddr,myaddr) == 0);
|
|
}
|
|
|
|
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)
|
|
{
|
|
uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid;
|
|
//char str[65],str2[65];
|
|
if ( DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout.size() > 0 )
|
|
{
|
|
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 )
|
|
{
|
|
hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash);
|
|
if ( hentropy == hentropy2 )
|
|
{
|
|
winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
|
|
//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);
|
|
if ( winnings == 0 )
|
|
{
|
|
// queue 'L' losing tx
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
// queue 'W' winning tx
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
|
|
{
|
|
uint256 txid,fundingtxid,hashBlock,hash,proof,entropy; 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;
|
|
numvins = tx.vin.size();
|
|
numvouts = tx.vout.size();
|
|
preventCCvins = preventCCvouts = -1;
|
|
if ( numvouts < 1 )
|
|
return eval->Invalid("no vouts");
|
|
else
|
|
{
|
|
txid = tx.GetHash();
|
|
if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
|
|
{
|
|
if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
|
|
return eval->Invalid("cant find fundingtxid");
|
|
else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
|
|
return eval->Invalid("fundingTx not valid");
|
|
switch ( funcid )
|
|
{
|
|
case 'F':
|
|
//vins.*: normal inputs
|
|
//vout.0: CC vout for funding
|
|
//vout.1: normal marker vout for easy searching
|
|
//vout.2: normal change
|
|
//vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks
|
|
return eval->Invalid("unexpected DiceValidate for createfunding");
|
|
break;
|
|
case 'E': // check sig of vin to match fundingtxid
|
|
//vins.*: normal inputs
|
|
//vout.0: CC vout for locked entropy funds
|
|
//vout.1: tag to owner address for entropy funds
|
|
//vout.2: normal change
|
|
//vout.n-1: opreturn 'E' sbits fundingtxid hentropy
|
|
return eval->Invalid("unexpected DiceValidate for addfunding entropy");
|
|
break;
|
|
case 'B':
|
|
//vin.0: entropy txid from house
|
|
//vins.1+: normal inputs
|
|
//vout.0: CC vout for locked entropy
|
|
//vout.1: CC vout for locked bet
|
|
//vout.2: tag for bettor's address (txfee + odds)
|
|
//vout.3: change
|
|
//vout.n-1: opreturn 'B' sbits fundingtxid entropy
|
|
if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
|
|
return eval->Invalid("always should find vin.0, but didnt");
|
|
if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
|
|
{
|
|
fprintf(stderr,"DiceIsWinner.%d\n",iswin);
|
|
DiceQueue(iswin,sbits,fundingtxid,txid);
|
|
}
|
|
//return eval->Invalid("dont confirm bet during debug");
|
|
break;
|
|
case 'L':
|
|
//vin.0: betTx CC vout.0 entropy from bet
|
|
//vin.1: betTx CC vout.1 bet amount from bet
|
|
//vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
//vout.0: funding CC to entropy owner
|
|
//vout.1: tag to owner address for entropy funds
|
|
//vout.n-1: opreturn 'L' sbits fundingtxid hentropy
|
|
break;
|
|
case 'W':
|
|
//vin.0: betTx CC vout.0 entropy from bet
|
|
//vin.1: betTx CC vout.1 bet amount from bet
|
|
//vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
//vout.0: funding CC change to entropy owner
|
|
//vout.1: tag to owner address for entropy funds
|
|
//vout.2: normal output to bettor's address
|
|
//vout.n-1: opreturn 'W' sbits fundingtxid hentropy
|
|
break;
|
|
case 'T':
|
|
//vin.0: betTx CC vout.0 entropy from bet
|
|
//vin.1: betTx CC vout.1 bet amount from bet
|
|
//vin.2+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T'
|
|
//vout.0: funding CC vin.0 to entropy owner
|
|
//vout.1: tag to owner address for entropy funds
|
|
//vout.2: normal vin.1 to bettor's address
|
|
//vout.n-1: opreturn 'T' sbits fundingtxid hentropy
|
|
/*for (i=0; i<numvins; i++)
|
|
{
|
|
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
|
|
return eval->Invalid("unexpected normal vin for unlock");
|
|
}
|
|
if ( DiceExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
|
|
return false;
|
|
else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
|
|
return eval->Invalid("always should find vin.0, but didnt");
|
|
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
|
return eval->Invalid("lock tx vout.0 is normal output");
|
|
else if ( tx.vout.size() < 3 )
|
|
return eval->Invalid("unlock tx not enough vouts");
|
|
else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
|
return eval->Invalid("unlock tx vout.0 is normal output");
|
|
else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
|
return eval->Invalid("unlock tx vout.1 is CC output");
|
|
else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
|
|
return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
|
|
amount = vinTx.vout[0].nValue;
|
|
reward = 0;
|
|
//reward = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,timeoutblocks);
|
|
if ( tx.vout[1].nValue > amount+reward )
|
|
return eval->Invalid("unlock tx vout.1 isnt amount+reward");
|
|
preventCCvouts = 1;*/
|
|
break;
|
|
}
|
|
}
|
|
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
uint64_t AddDiceInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
|
|
{
|
|
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;
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
GetCCaddress(cp,coinaddr,pk);
|
|
SetCCunspents(unspentOutputs,coinaddr);
|
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
|
{
|
|
txid = it->first.txhash;
|
|
vout = (int32_t)it->first.index;
|
|
if ( it->second.satoshis < 1000000 )
|
|
continue;
|
|
fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
|
|
for (j=0; j<mtx.vin.size(); j++)
|
|
if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
|
|
break;
|
|
if ( j != mtx.vin.size() )
|
|
continue;
|
|
if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
|
{
|
|
if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
|
|
{
|
|
fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
|
|
if ( fundsflag != 0 && funcid != 'F' && funcid != 'E' && funcid != 'W' && funcid != 'L' && funcid != 'T' )
|
|
continue;
|
|
else if ( fundsflag == 0 && funcid != 'B' )
|
|
continue;
|
|
if ( total != 0 && maxinputs != 0 )
|
|
{
|
|
if ( fundsflag == 0 )
|
|
scriptPubKey = tx.vout[1].scriptPubKey;
|
|
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
|
}
|
|
totalinputs += it->second.satoshis;
|
|
n++;
|
|
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
|
break;
|
|
} else fprintf(stderr,"null funcid\n");
|
|
}
|
|
}
|
|
return(totalinputs);
|
|
}
|
|
|
|
uint64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
|
|
{
|
|
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;
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
GetCCaddress(cp,coinaddr,pk);
|
|
SetCCunspents(unspentOutputs,coinaddr);
|
|
entropyval = 0;
|
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
|
{
|
|
txid = it->first.txhash;
|
|
vout = (int32_t)it->first.index;
|
|
if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
|
{
|
|
char str[65],str2[65];
|
|
if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
|
|
{
|
|
if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
|
|
{
|
|
if ( funcid != 'B' && refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 )
|
|
{
|
|
totalinputs += nValue;
|
|
fprintf(stderr,"add %.8f\n",(double)nValue/COIN);
|
|
if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
|
|
{
|
|
entropytxid = txid;
|
|
entropyval = tx.vout[0].nValue;
|
|
first = 1;
|
|
}
|
|
} //else fprintf(stderr,"%c refsbits.%llx sbits.%llx nValue %.8f\n",funcid,(long long)refsbits,(long long)sbits,(double)nValue/COIN);
|
|
} //else fprintf(stderr,"else case funcid (%c) %d %s vs %s\n",funcid,funcid,uint256_str(str,reffundingtxid),uint256_str(str2,fundingtxid));
|
|
} //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
|
|
}
|
|
}
|
|
return(totalinputs);
|
|
}
|
|
|
|
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)
|
|
{
|
|
char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
|
|
GetCCaddress(cp,CCaddr,dicepk);
|
|
SetCCtxids(txids,cp->normaladdr);
|
|
if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
|
|
{
|
|
if ( GetTransaction(fundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
|
|
{
|
|
if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
|
|
{
|
|
fundingPubKey = tx.vout[1].scriptPubKey;
|
|
return(true);
|
|
}
|
|
}
|
|
return(false);
|
|
}
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
|
{
|
|
//int height = it->first.blockHeight;
|
|
txid = it->first.txhash;
|
|
if ( fundingtxid != zeroid && txid != fundingtxid )
|
|
continue;
|
|
if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
|
|
{
|
|
if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
|
|
{
|
|
if ( sbits == refsbits )
|
|
{
|
|
fundingPubKey = tx.vout[1].scriptPubKey;
|
|
fundingtxid = txid;
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct CCcontract_info *cp; int32_t cmpflag;
|
|
cp = CCinit(C,EVAL_DICE);
|
|
if ( txfee == 0 )
|
|
txfee = 10000;
|
|
mypk = pubkey2pk(Mypubkey());
|
|
dicepk = GetUnspendable(cp,0);
|
|
sbits = stringbits(planstr);
|
|
if ( reffundingtxid == zeroid )
|
|
cmpflag = 0;
|
|
else cmpflag = 1;
|
|
if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
|
|
{
|
|
fprintf(stderr,"Dice plan (%s) already exists cmpflag.%d\n",planstr,cmpflag);
|
|
return(0);
|
|
}
|
|
return(cp);
|
|
}
|
|
|
|
UniValue DiceInfo(uint256 diceid)
|
|
{
|
|
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;
|
|
if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 )
|
|
{
|
|
fprintf(stderr,"cant find fundingtxid\n");
|
|
result.push_back(Pair("error","cant find fundingtxid"));
|
|
return(result);
|
|
}
|
|
if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
|
|
{
|
|
fprintf(stderr,"fundingtxid isnt dice creation txid\n");
|
|
result.push_back(Pair("error","fundingtxid isnt dice creation txid"));
|
|
return(result);
|
|
}
|
|
result.push_back(Pair("result","success"));
|
|
result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
|
|
unstringbits(str,sbits);
|
|
result.push_back(Pair("name",str));
|
|
result.push_back(Pair("sbits",sbits));
|
|
sprintf(numstr,"%.8f",(double)minbet/COIN);
|
|
result.push_back(Pair("minbet",numstr));
|
|
sprintf(numstr,"%.8f",(double)maxbet/COIN);
|
|
result.push_back(Pair("maxbet",numstr));
|
|
result.push_back(Pair("maxodds",maxodds));
|
|
result.push_back(Pair("timeoutblocks",timeoutblocks));
|
|
cp = CCinit(&C,EVAL_DICE);
|
|
dicepk = GetUnspendable(cp,0);
|
|
funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid);
|
|
sprintf(numstr,"%.8f",(double)funding/COIN);
|
|
result.push_back(Pair("funding",numstr));
|
|
return(result);
|
|
}
|
|
|
|
UniValue DiceList()
|
|
{
|
|
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];
|
|
cp = CCinit(&C,EVAL_DICE);
|
|
SetCCtxids(addressIndex,cp->normaladdr);
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
|
|
{
|
|
txid = it->first.txhash;
|
|
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
|
|
{
|
|
if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
|
|
{
|
|
result.push_back(uint256_str(str,txid));
|
|
}
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
|
|
{
|
|
CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C;
|
|
if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || timeoutblocks < 0 || timeoutblocks > 1440 )
|
|
{
|
|
fprintf(stderr,"negative parameter error\n");
|
|
return(0);
|
|
}
|
|
memset(&zero,0,sizeof(zero));
|
|
if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
|
|
return(0);
|
|
if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
|
|
{
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk));
|
|
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
|
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
|
|
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
|
|
}
|
|
fprintf(stderr,"cant find enough inputs\n");
|
|
return(0);
|
|
}
|
|
|
|
std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
|
|
{
|
|
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;
|
|
if ( amount < 0 )
|
|
{
|
|
fprintf(stderr,"negative parameter error\n");
|
|
return(0);
|
|
}
|
|
if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
|
|
return(0);
|
|
scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
|
|
if ( 0 )
|
|
{
|
|
uint8_t *ptr0,*ptr1; int32_t i;
|
|
ptr0 = (uint8_t *)scriptPubKey.data();
|
|
ptr1 = (uint8_t *)fundingPubKey.data();
|
|
for (i=0; i<35; i++)
|
|
fprintf(stderr,"%02x",ptr0[i]);
|
|
fprintf(stderr," script vs ");
|
|
for (i=0; i<35; i++)
|
|
fprintf(stderr,"%02x",ptr1[i]);
|
|
fprintf(stderr," funding\n");
|
|
}
|
|
if ( scriptPubKey == fundingPubKey )
|
|
{
|
|
if ( AddNormalinputs(mtx,mypk,amount+2*txfee,64) > 0 )
|
|
{
|
|
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
|
|
mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
|
|
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid)));
|
|
} else fprintf(stderr,"cant find enough inputs\n");
|
|
} else fprintf(stderr,"only fund creator can add more funds (entropy)\n");
|
|
return(0);
|
|
}
|
|
|
|
std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
|
|
{
|
|
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;
|
|
if ( bet < 0 || odds < 1 )
|
|
{
|
|
fprintf(stderr,"negative parameter error\n");
|
|
return(0);
|
|
}
|
|
if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
|
|
return(0);
|
|
if ( bet < minbet || bet > maxbet || odds > maxodds )
|
|
{
|
|
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);
|
|
return(0);
|
|
}
|
|
if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 )
|
|
{
|
|
mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
|
|
if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
|
|
{
|
|
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
|
|
mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
|
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
|
|
} else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN);
|
|
}
|
|
if ( entropyval == 0 && funding != 0 )
|
|
fprintf(stderr,"cant find dice entropy inputs\n");
|
|
else fprintf(stderr,"cant find dice inputs\n");
|
|
return(0);
|
|
}
|
|
|
|
std::string DiceWinLoseTimeout(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout)
|
|
{
|
|
CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; 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;
|
|
*resultp = 0;
|
|
if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
|
|
return(0);
|
|
if ( winlosetimeout != 0 )
|
|
{
|
|
scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
|
|
if ( scriptPubKey != fundingPubKey )
|
|
{
|
|
fprintf(stderr,"only dice fund creator can submit winner or loser\n");
|
|
return(0);
|
|
}
|
|
}
|
|
if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 )
|
|
{
|
|
bettorentropy = DiceGetEntropy(betTx,'B');
|
|
if ( (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
|
|
{
|
|
if ( iswin == winlosetimeout )
|
|
{
|
|
fprintf(stderr,"iswin.%d matches\n",iswin);
|
|
mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
|
|
mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
|
|
if ( iswin == 0 )
|
|
{
|
|
funcid = 'T';
|
|
fprintf(stderr,"timeout refunds are not supported yet\n");
|
|
}
|
|
else if ( iswin > 0 )
|
|
{
|
|
funcid = 'W';
|
|
odds = (betTx.vout[2].nValue - txfee);
|
|
if ( odds < 1 || odds > maxodds )
|
|
{
|
|
fprintf(stderr,"illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
|
|
return(0);
|
|
}
|
|
CCchange = betTx.vout[0].nValue;
|
|
fundsneeded = 2*txfee + (odds-1)*betTx.vout[1].nValue;
|
|
if ( (inputs= AddDiceInputs(scriptPubKey,1,cp,mtx,dicepk,fundsneeded,60)) > 0 )
|
|
{
|
|
if ( inputs > fundsneeded+txfee )
|
|
CCchange += (inputs - (fundsneeded+txfee));
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
|
|
mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
|
|
mtx.vout.push_back(CTxOut(odds * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,"not enough inputs for %.8f\n",(double)fundsneeded/COIN);
|
|
return(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
funcid = 'L';
|
|
mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue - txfee,dicepk));
|
|
mtx.vout.push_back(CTxOut(txfee,entropyTx.vout[1].scriptPubKey));
|
|
}
|
|
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
|
|
*resultp = 1;
|
|
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
|
|
} else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
|
|
} else return(0);
|
|
}
|
|
return(0);
|
|
}
|
|
|