diff --git a/src/cc/CCdice.h b/src/cc/CCdice.h index 7c89d18d5..23adb0ffb 100644 --- a/src/cc/CCdice.h +++ b/src/cc/CCdice.h @@ -23,7 +23,10 @@ bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); -std::string DiceFund(uint64_t txfee,uint64_t funds); -std::string DiceBet(uint64_t txfee,uint64_t amount,uint64_t odds); +std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds); +std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks); +std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount); +UniValue DiceInfo(uint256 diceid); +UniValue DiceList(); #endif diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index 159ac7a8c..0e6669964 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -14,12 +14,87 @@ ******************************************************************************/ #include "CCdice.h" -#include "../txmempool.h" /* -*/ + 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) + half of shared secret + 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout. + 3. house account sends funds to winner with proof of entropy + 4. if timeout, bettor wins funds + + 2. and 3. can be done in mempool + */ -// start of consensus code +uint64_t DiceCalc(uint64_t amount,uint256 txid,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks) +{ + /*uint64_t duration,reward = 0; + if ( (duration= CCduration(txid)) < minseconds ) + { + return(0); + //duration = (uint32_t)time(NULL) - (1532713903 - 3600 * 24); + } else if ( duration > maxseconds ) + maxseconds = duration; + reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100); + fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN); + return(reward);*/ + return(0); +} + +CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t forfeitblocks) +{ + CScript opret; uint8_t evalcode = EVAL_DICE; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << forfeitblocks); + return(opret); +} + +uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &forfeitblocks) +{ + std::vector 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 >> forfeitblocks) != 0 ) + { + if ( e == EVAL_DICE && f == 'F' ) + return(f); + } + return(0); +} + +CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid) +{ + CScript opret; uint8_t evalcode = EVAL_DICE; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid); + return(opret); +} + +uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid) +{ + std::vector vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,forfeitblocks; + 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 >> forfeitblocks) != 0 ) + { + 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) != 0 ) + { + if ( e == EVAL_DICE && (f == 'L' || f == 'U' || f == 'A') ) + 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); +} uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { @@ -32,23 +107,20 @@ uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) return(0); } -bool DiceExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +bool DiceExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee) { static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis; + 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; iismyvin)(tx.vin[i].scriptSig) != 0 ) { - //fprintf(stderr,"vini.%d check mempool\n",i); if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); + return eval->Invalid("always should find vin, but didnt"); else { - //fprintf(stderr,"vini.%d check hash and vout\n",i); if ( hashBlock == zerohash ) return eval->Invalid("cant dice from mempool"); if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) @@ -62,17 +134,17 @@ bool DiceExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( (assetoshis= IsDicevout(cp,tx,i)) != 0 ) outputs += assetoshis; } - if ( inputs != outputs+COIN+txfee ) + if ( inputs != outputs+txfee ) { fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); - return eval->Invalid("mismatched inputs != outputs + COIN + txfee"); + return eval->Invalid("mismatched inputs != outputs + txfee"); } else return(true); } bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; + uint256 txid,fundingtxid,hashBlock; 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; numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -80,104 +152,361 @@ bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) return eval->Invalid("no vouts"); else { - //fprintf(stderr,"check vins\n"); - for (i=0; iGetTxUnconfirmed(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,forfeitblocks) != 'F' ) + return eval->Invalid("fundingTx not valid"); + switch ( funcid ) { - fprintf(stderr,"diceget invalid vini\n"); - return eval->Invalid("illegal normal vini"); + 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 APR minseconds maxseconds mindeposit + return eval->Invalid("unexpected DiceValidate for createfunding"); + break; + case 'A': + //vins.*: normal inputs + //vout.0: CC vout for funding + //vout.1: normal change + //vout.n-1: opreturn 'A' sbits fundingtxid + return eval->Invalid("unexpected DiceValidate for addfunding"); + break; + case 'L': + //vins.*: normal inputs + //vout.0: CC vout for locked funds + //vout.1: normal output to unlock address + //vout.2: change + //vout.n-1: opreturn 'L' sbits fundingtxid + return eval->Invalid("unexpected DiceValidate for lock"); + break; + case 'U': + //vin.0: locked funds CC vout.0 from lock + //vin.1+: funding CC vout.0 from 'F' and 'A' and 'U' + //vout.0: funding CC change + //vout.1: normal output to unlock address + //vout.n-1: opreturn 'U' sbits fundingtxid + for (i=0; iismyvin)(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 = DiceCalc(amount,tx.vin[0].prevout.hash,minbet,maxbet,maxodds,forfeitblocks); + if ( tx.vout[1].nValue > amount+reward ) + return eval->Invalid("unlock tx vout.1 isnt amount+reward"); + preventCCvouts = 1; + break; } } - //fprintf(stderr,"check amounts\n"); - if ( DiceExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"diceget invalid amount\n"); - return false; - } - else - { - preventCCvouts = 1; - if ( IsDicevout(cp,tx,0) != 0 ) - { - preventCCvouts++; - i = 1; - } else i = 0; - if ( tx.vout[i].nValue != COIN ) - return eval->Invalid("invalid dice output"); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"diceget validated\n"); - else fprintf(stderr,"diceget invalid\n"); - return(retval); - } + return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); } + return(true); } -// end of consensus code -// helper functions for rpc calls in rpcwallet.cpp - -uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs) +// 'L' vs 'F' and 'A' +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]; uint64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; + char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; - // prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + vout = (int32_t)it->first.index; + fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + for (j=0; j 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( (nValue= IsDicevout(cp,vintx,(int32_t)it->first.index)) > 0 ) + if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 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 != 'A' && funcid != 'U' ) + continue; + else if ( fundsflag == 0 && (funcid != 'L' || tx.vout.size() < 4) ) + continue; if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; + { + 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); } -std::string DiceBet(uint64_t txfee,uint64_t amount,uint64_t odds) +uint64_t DicePlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid) { - CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_DICE); - if ( txfee == 0 ) - txfee = 10000; - dicepk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddDiceInputs(cp,mtx,dicepk,nValue+txfee,60)) > 0 ) + char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid; + std::vector > unspentOutputs; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_DICE,CCchange,dicepk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); - } else fprintf(stderr,"cant find dice inputs\n"); - return(0); -} - -std::string DiceFund(uint64_t txfee,uint64_t funds) -{ - CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_DICE); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - dicepk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_DICE,funds,dicepk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 ) + { + if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) + { + if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout)) > 0 ) + totalinputs += nValue; + else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN); + } //else fprintf(stderr,"else case\n"); + } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN); + } } + return(totalinputs); +} + +bool DicePlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &forfeitblocks) +{ + char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx; + std::vector > txids; + GetCCaddress(cp,CCaddr,dicepk); + SetCCtxids(txids,CCaddr); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + //int height = it->first.blockHeight; + txid = it->first.txhash; + 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,forfeitblocks) == 'F' ) + { + if ( sbits == refsbits ) + return(true); + } + } + } + return(false); +} + +UniValue DiceInfo(uint256 diceid) +{ + UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; int64_t minbet,maxbet,maxodds,forfeitblocks; uint64_t sbits; char str[67],numstr[65]; + 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,forfeitblocks) == 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("forfeitblocks",forfeitblocks)); + sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue/COIN); + result.push_back(Pair("funding",numstr)); + return(result); +} + +UniValue DiceList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,forfeitblocks; char str[65]; + cp = CCinit(&C,EVAL_DICE); + SetCCtxids(addressIndex,cp->normaladdr); + for (std::vector >::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,forfeitblocks) != 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 forfeitblocks) +{ + CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t sbits; int64_t a,b,c,d; struct CCcontract_info *cp,C; + if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || forfeitblocks < 0 || forfeitblocks > 1440 ) + { + fprintf(stderr,"negative parameter error\n"); + return(0); + } + cp = CCinit(&C,EVAL_DICE); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + dicepk = GetUnspendable(cp,0); + sbits = stringbits(planstr); + if ( DicePlanExists(cp,sbits,dicepk,a,b,c,d) != 0 ) + { + fprintf(stderr,"Dice plan (%s) already exists\n",planstr); + return(0); + } + if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk)); + 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,forfeitblocks))); + } + fprintf(stderr,"cant find enough inputs\n"); return(0); } +std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount) +{ + CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t sbits; int64_t a,b,c,d; struct CCcontract_info *cp,C; + if ( amount < 0 ) + { + fprintf(stderr,"negative parameter error\n"); + return(0); + } + cp = CCinit(&C,EVAL_DICE); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + dicepk = GetUnspendable(cp,0); + sbits = stringbits(planstr); + if ( DicePlanExists(cp,sbits,dicepk,a,b,c,d) == 0 ) + { + fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); + return(0); + } + sbits = stringbits(planstr); + if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('A',sbits,fundingtxid))); + } else fprintf(stderr,"cant find enough inputs\n"); + fprintf(stderr,"cant find fundingtxid\n"); + return(0); +} + +std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds) +{ + CMutableTransaction mtx; CPubKey mypk,dicepk; CScript opret; uint64_t sbits; int64_t funding,minbet,maxbet,maxodds,forfeitblocks; struct CCcontract_info *cp,C; + if ( bet < 0 || odds < 1 ) + { + fprintf(stderr,"negative parameter error\n"); + return(0); + } + cp = CCinit(&C,EVAL_DICE); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + dicepk = GetUnspendable(cp,0); + sbits = stringbits(planstr); + if ( DicePlanExists(cp,sbits,dicepk,minbet,maxbet,maxodds,forfeitblocks) == 0 ) + { + fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); + return(0); + } + if ( bet < minbet ) + { + fprintf(stderr,"Dice plan %s bet %.8f < minbet %.8f\n",planstr,(double)bet/COIN,(double)minbet/COIN); + return(0); + } + if ( (funding= DicePlanFunds(sbits,cp,dicepk,fundingtxid)) >= bet*odds+txfee ) + { + if ( AddNormalinputs(mtx,mypk,bet+2*txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('L',sbits,fundingtxid))); + } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)bet/COIN); + } + fprintf(stderr,"cant find dice inputs\n"); + return(0); +} + +std::string DiceUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid) +{ + CMutableTransaction mtx; CTransaction tx; char coinaddr[64]; CPubKey mypk,dicepk; CScript opret,scriptPubKey,ignore; uint256 hashBlock; uint64_t funding,sbits,reward=0,amount=0,inputs,CCchange=0; int64_t minbet,maxbet,maxodds,forfeitblocks; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_DICE); + if ( txfee == 0 ) + txfee = 10000; + dicepk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + sbits = stringbits(planstr); + if ( DicePlanExists(cp,sbits,dicepk,minbet,maxbet,maxodds,forfeitblocks) == 0 ) + { + fprintf(stderr,"Dice plan %s doesnt exist\n",planstr); + return(0); + } + // need to deal with finding the right utxos + if ( locktxid == zeroid ) + amount = AddDiceInputs(scriptPubKey,0,cp,mtx,dicepk,(1LL << 30),1); + else + { + GetCCaddress(cp,coinaddr,dicepk); + if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 ) + { + fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr); + return(0); + } + if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) + { + scriptPubKey = tx.vout[1].scriptPubKey; + mtx.vin.push_back(CTxIn(locktxid,0,CScript())); + } + else + { + fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr); + return(0); + } + } + if ( amount > 0 && (reward= DiceCalc(amount,mtx.vin[0].prevout.hash,minbet,maxbet,maxodds,forfeitblocks)) > txfee && scriptPubKey.size() > 0 ) + { + if ( (inputs= AddDiceInputs(ignore,1,cp,mtx,dicepk,reward+txfee,30)) > 0 ) + { + if ( inputs >= (reward + 2*txfee) ) + CCchange = (inputs - (reward + txfee)); + fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk)); + mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey)); + return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeDiceOpRet('U',sbits,fundingtxid))); + } + fprintf(stderr,"cant find enough dice inputs\n"); + } + fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN); + return(0); +} diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 2944c1bee..ba7cd5657 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -69,12 +69,13 @@ uint64_t RewardsCalc(uint64_t amount,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit) { uint64_t duration,reward = 0; + fprintf(stderr,"minseconds %llu maxseconds %llu\n",(long long)minseconds,(long long)maxseconds); if ( (duration= CCduration(txid)) < minseconds ) { return(0); //duration = (uint32_t)time(NULL) - (1532713903 - 3600 * 24); } else if ( duration > maxseconds ) - maxseconds = duration; + duration = maxseconds; reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100); fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN); return(reward); @@ -264,7 +265,7 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t // 'L' vs 'F' and 'A' uint64_t AddRewardsInputs(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,APR,minseconds,maxseconds,mindeposit,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; + char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); @@ -305,7 +306,7 @@ uint64_t AddRewardsInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontr uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid) { - char coinaddr[64]; uint64_t sbits,APR,minseconds,maxseconds,mindeposit,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid; + char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); @@ -322,7 +323,7 @@ uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey p if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout)) > 0 ) totalinputs += nValue; else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN); - } else fprintf(stderr,"else case\n"); + } //else fprintf(stderr,"else case\n"); } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN); } } @@ -341,6 +342,7 @@ bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewa txid = it->first.txhash; if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) { + //char str[65]; fprintf(stderr,"rewards plan %s\n",uint256_str(str,txid)); if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' ) { if ( sbits == refsbits ) @@ -510,7 +512,7 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr); return(0); } - // need to deal with finding the right utxos + fprintf(stderr,"APR %.8f minseconds.%llu maxseconds.%llu mindeposit %.8f\n",(double)APR/COIN,(long long)minseconds,(long long)maxseconds,(double)mindeposit/COIN); if ( locktxid == zeroid ) amount = AddRewardsInputs(scriptPubKey,0,cp,mtx,rewardspk,(1LL << 30),1); else diff --git a/src/main.cpp b/src/main.cpp index 5371bed51..79182bad8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4551,11 +4551,11 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo auto verifier = libzcash::ProofVerifier::Disabled(); hash = pblock->GetHash(); //fprintf(stderr,"ProcessBlock %d\n",(int32_t)chainActive.LastTip()->nHeight); - if ( chainActive.LastTip() != 0 ) - komodo_currentheight_set(chainActive.LastTip()->nHeight); - checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); { LOCK(cs_main); + if ( chainActive.LastTip() != 0 ) + komodo_currentheight_set(chainActive.LastTip()->nHeight); + checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); bool fRequested = MarkBlockAsReceived(hash); fRequested |= fForceProcessing; if ( checked != 0 && komodo_checkPOW(0,pblock,height) < 0 ) //from_miner && ASSETCHAINS_STAKED == 0 diff --git a/src/miner.cpp b/src/miner.cpp index 1750e27ca..fb2eb2d05 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -493,8 +493,23 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn,int32_t gpucount) return(0); } } + else if ( ASSETCHAINS_CC == 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) ) + { + CValidationState state; + //fprintf(stderr,"check validity\n"); + if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks + { + //static uint32_t counter; + //if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 ) + // fprintf(stderr,"warning: miner testblockvalidity failed\n"); + fprintf(stderr,"invalid\n"); + return(0); + } + //fprintf(stderr,"valid\n"); + } } - if ( pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) ) + /* skip checking validity outside of lock. if inside lock and CC contract is being validated, can deadlock. + if ( ASSETCHAINS_CC != 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) ) { CValidationState state; //fprintf(stderr,"check validity\n"); @@ -507,7 +522,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn,int32_t gpucount) return(0); } //fprintf(stderr,"valid\n"); - } + }*/ //fprintf(stderr,"done new block\n"); return pblocktemplate.release(); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index a7e16652a..580a050c3 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -368,7 +368,10 @@ static const CRPCCommand vRPCCommands[] = { "faucet", "faucetaddress", &faucetaddress, true }, /* dice */ + { "dice", "dicelist", &dicelist, true }, + { "dice", "diceinfo", &diceinfo, true }, { "dice", "dicefund", &dicefund, true }, + { "dice", "diceaddfunds", &diceaddfunds, true }, { "dice", "dicebet", &dicebet, true }, { "dice", "diceaddress", &diceaddress, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 9144b874a..4da7c73c0 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -235,6 +235,9 @@ extern UniValue rewardslock(const UniValue& params, bool fHelp); extern UniValue rewardsunlock(const UniValue& params, bool fHelp); extern UniValue diceaddress(const UniValue& params, bool fHelp); extern UniValue dicefund(const UniValue& params, bool fHelp); +extern UniValue dicelist(const UniValue& params, bool fHelp); +extern UniValue diceinfo(const UniValue& params, bool fHelp); +extern UniValue diceaddfunds(const UniValue& params, bool fHelp); extern UniValue dicebet(const UniValue& params, bool fHelp); extern UniValue lottoaddress(const UniValue& params, bool fHelp); extern UniValue ponziaddress(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae9c9b784..0bc73e9f1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5029,7 +5029,7 @@ UniValue rewardsaddfunding(const UniValue& params, bool fHelp) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); - } else result.push_back(Pair("error", "couldnt create rewards lock transaction")); + } else result.push_back(Pair("error", "couldnt create rewards addfunding transaction")); return(result); } @@ -5054,6 +5054,27 @@ UniValue rewardsunlock(const UniValue& params, bool fHelp) return(result); } +UniValue rewardslist(const UniValue& params, bool fHelp) +{ + uint256 tokenid; + if ( fHelp || params.size() > 0 ) + throw runtime_error("rewardslist\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + return(RewardsList()); +} + +UniValue rewardsinfo(const UniValue& params, bool fHelp) +{ + uint256 fundingtxid; + if ( fHelp || params.size() != 1 ) + throw runtime_error("rewardsinfo fundingtxid\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + return(RewardsInfo(fundingtxid)); +} + UniValue faucetfund(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); uint64_t funds; std::string hex; @@ -5089,33 +5110,57 @@ UniValue faucetget(const UniValue& params, bool fHelp) UniValue dicefund(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); uint64_t funds; std::string hex; - if ( fHelp || params.size() > 1 ) - throw runtime_error("faucetfund amount\n"); + UniValue result(UniValue::VOBJ); int64_t funds,minbet,maxbet,maxodds,forfeitblocks; std::string hex; char *name; + if ( fHelp || params.size() != 6 ) + throw runtime_error("dicefund name funds minbet maxbet maxodds forfeitblocks\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - funds = atof(params[0].get_str().c_str()) * COIN; - hex = DiceFund(0,funds); + name = (char *)params[0].get_str().c_str(); + funds = atof(params[1].get_str().c_str()) * COIN; + minbet = atof(params[2].get_str().c_str()) * COIN; + maxbet = atof(params[3].get_str().c_str()) * COIN; + maxodds = atol(params[4].get_str().c_str()); + forfeitblocks = atol(params[5].get_str().c_str()); + hex = DiceCreateFunding(0,name,funds,minbet,maxbet,maxodds,forfeitblocks); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); - } else result.push_back(Pair("error", "couldnt create faucet funding transaction")); + } else result.push_back(Pair("error", "couldnt create dice funding transaction")); + return(result); +} + +UniValue diceaddfunds(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; uint64_t amount; std::string hex; + if ( fHelp || params.size() != 3 ) + throw runtime_error("diceaddfunds name fundingtxid amount\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + name = (char *)params[0].get_str().c_str(); + fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); + amount = atof(params[2].get_str().c_str()) * COIN; + hex = DiceAddfunding(0,name,fundingtxid,amount); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else result.push_back(Pair("error", "couldnt create dice addfunding transaction")); return(result); } UniValue dicebet(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string hex; uint64_t amount,odds; - if ( fHelp || params.size() > 2 ) - throw runtime_error("dicebet amount odds\n"); + UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid; uint64_t amount,odds; char *name; + if ( fHelp || params.size() != 4 ) + throw runtime_error("dicebet name fundingtxid amount odds\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - amount = atof(params[0].get_str().c_str()) * COIN; - if ( params.size() == 2 ) - odds = atof(params[1].get_str().c_str()) * COIN; - else odds = 1; - hex = DiceBet(0,amount,odds); + name = (char *)params[0].get_str().c_str(); + fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); + amount = atof(params[2].get_str().c_str()) * COIN; + odds = atol(params[3].get_str().c_str()); + hex = DiceBet(0,name,fundingtxid,amount,odds); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5124,25 +5169,25 @@ UniValue dicebet(const UniValue& params, bool fHelp) return(result); } -UniValue rewardslist(const UniValue& params, bool fHelp) +UniValue dicelist(const UniValue& params, bool fHelp) { uint256 tokenid; if ( fHelp || params.size() > 0 ) - throw runtime_error("rewardslist\n"); + throw runtime_error("dicelist\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - return(RewardsList()); + return(DiceList()); } -UniValue rewardsinfo(const UniValue& params, bool fHelp) +UniValue diceinfo(const UniValue& params, bool fHelp) { uint256 fundingtxid; if ( fHelp || params.size() != 1 ) - throw runtime_error("rewardsinfo fundingtxid\n"); + throw runtime_error("diceinfo fundingtxid\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(RewardsInfo(fundingtxid)); + return(DiceInfo(fundingtxid)); } UniValue tokenlist(const UniValue& params, bool fHelp)