From e0f78699ce26d15faab9549a2114f50d564fa8e1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 21 Aug 2018 02:30:05 -1100 Subject: [PATCH] Ccactivate --- src/cc/CCinclude.h | 3 +- src/cc/CClotto.h | 3 + src/cc/CCutils.cpp | 2 + src/cc/lotto.cpp | 172 +++++++++++++++++++++++++++++++++++++--- src/komodo_gateway.h | 1 + src/komodo_globals.h | 2 +- src/komodo_utils.h | 3 + src/main.h | 4 +- src/script/standard.cpp | 6 +- 9 files changed, 179 insertions(+), 17 deletions(-) diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 761e64bcd..96e1b6d17 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -28,7 +28,8 @@ #include #include -extern int32_t KOMODO_CONNECTING; +extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE; +extern uint32_t ASSETCHAINS_CC; #define SMALLVAL 0.000000000000001 union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; diff --git a/src/cc/CClotto.h b/src/cc/CClotto.h index 41e3cb5b1..2b33769c5 100644 --- a/src/cc/CClotto.h +++ b/src/cc/CClotto.h @@ -20,9 +20,12 @@ #include "CCinclude.h" #define EVAL_LOTTO 0xe9 +uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv); bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +UniValue LottoInfo(uint256 lottoid); +UniValue LottoList(); std::string LottoTicket(uint64_t txfee,int64_t numtickets); std::string LottoWinner(uint64_t txfee); diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 54a9a0225..4ca2f1c63 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -256,6 +256,8 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv) bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) { CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; + if ( ASSETCHAINS_CC == 0 || height < KOMODO_CCACTIVATE ) + return(false); if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation return(true); height = KOMODO_CONNECTING; diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index e20cb3505..8007b42fc 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -17,6 +17,48 @@ #include "../txmempool.h" /* + A blockchain lotto has the problem of generating the deterministic random numbers needed to get a winner in a way that doesnt allow cheating. If we save the entropy for later publishing and display the hash of the entropy, it is true that the players wont know what the entropy value is, however the creator of the lotto funds will be able to know and simply create a winning ticket when the jackpot is large enough. + + We also need to avoid chain reorgs from disclosing the entropy and then allowing people to submit a winning ticket calculated based on the disclosed entropy (see attack vector in dice.cpp) + + As usual it needs to be provably fair and random + + The solution is for everybody to post the hash of their entropy when purchasing tickets. Then at the time of the drawing, nodes would post their entropy over an N block period to avoid censorship attack. After the N block period, then we have valid entropy that we know was locked in prior to the start of the N blocks and that nobody would have been able to know ahead of time the final entropy value. + + As long as one node submits a high entropy value, then just by combining all the submissions together, we get the drawing's entropy value. Given that, the usual process can determine if there was a winner at the specified odds. In fact, all the nodes are able to determine exactly how many winners there were and whether to validate 1/w payouts to the w winners or rollover the jackpot to the next drawing. + + To remove the need for an autopayout, the winning node(s) would need to submit a 1/w payout tx, this would be able to be done at any time and the winner does not have to have submitted proof of hentropy. In order to prevent a player from opportunistically withholding their entropy, the lotto creator will post the original proof of hentropy after the N block player submission period. This masks to all the players the final value of entropy. + + Attack vector: the lotto creator can have many player tickets in reserve all with their entropy ready to submit, but based on the actual submissions, find the one which gives him the best outcome. since all the player submissions will be known via mempool, along with the original hentropy. However the lotto creator would have to mine the final block in order to know the order of the player tickets. + + Thinking about this evil miner attack, it seems pretty bad, so a totally new approach is needed. Preferably with a simple enough protocol. Let us remove any special knowledge by the lotto creator, so like the faucet, it seems just that there is a single lotto for a chain. + + >>>>>>>>>>>> second iteration + + What we need is something that gives each ticket an equal chance at the jackpot, without allowing miner or relayer to gain an advantage. ultimately the jackpot payout tx needs to be confirmed, so there needs to be some number of blocks to make a claim to avoid censorship attack. If onchain entropy is needed, then it should be reduced to 1 bit per block to reduce the grinding that is possible. This does mean a block miner for the last bit of entropy can double their chances at winning, but the alternative is to have an external source of entropy, which creates its own set of issues like what prevents the nodes getting the external entropy from cheating? + + Conveniently the lotto mechanics are similar to a PoS staking, so it can be based on everybody trying to stake a single lotto jackpot. + + The calculation would need to be based on the payout address and utxosize, so relayers cant intercept it to steal the jackpot. + + each jackpot would effectively restart the lotto + + the funds from new lotto tickets can be spent by the jackpot, but those tickets can still win the new jackpot + + each set of tickets (utxo) would become eligible to claim the jackpot after some time is elapsed so the entropy for that utxo can be obtained. [6 bits * 32 + 1 bit * 16] 48 blocks + +It is possible to have a jackpot but miss out on it due to not claiming it. To minimize the effect from this, each ticket would have one chance to win, which can be calculated and a jackpot claim submitted just once. + + in order to randomize the timing of claim, a txid PoW similar to faucetget will maximize the chance of only a single jackpot txid that can propagate throughout the mempools, which will prevent the second one broadcast. Granted the mining node can override this if they also have a winning ticket, but assuming the PoS lottery makes it unlikely for two winners in a single block, this is not a big issue. + + In order to adapt the difficulty of winning the lotto, but not requiring recalculating all past tickets, as new lotto tickets are sold without a jackpot, it needs to become easier to win. Basically as the lotto jackpot gets bigger and bigger, it keeps getting easier to win! This convergence will avoid having unwinnable jackpots. + + rpc calls + lottoinfo + lottotickets + lottostatus + lottowinner tickethash ticketid + */ // start of consensus code @@ -62,10 +104,10 @@ bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction if ( (assetoshis= IsLottovout(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); } @@ -128,7 +170,7 @@ int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK { txid = it->first.txhash; // prevent dup - if ( it->second.satoshis < 1000000 ) + if ( it->second.satoshis < COIN ) continue; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { @@ -147,20 +189,128 @@ int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK return(totalinputs); } -std::string LottoTicket(uint64_t txfee,int64_t numtickets) +uint8_t DecodeLottoFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period,uint256 hentropy) { - CMutableTransaction mtx; CPubKey mypk,Lottopk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; + 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 >> ticketsize; ss >> odds; ss >> firstheight; ss >> period; ss >> hentropy) != 0 ) + { + if ( e == EVAL_LOTTO && f == 'F' ) + return(f); + } + return(0); +} + +int64_t LottoPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid) +{ + char coinaddr[64]; uint64_t sbits; int64_t nValue; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid; + std::vector > unspentOutputs; + lockedfunds = 0; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::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 ) + { + if ( (funcid= DecodeLottoOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'T' ) + { + if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) + { + if ( (nValue= IsLottovout(cp,tx,vout)) > 0 ) + lockedfunds += 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(lockedfunds); +} + +UniValue LottoInfo(uint256 lottoid) +{ + UniValue result(UniValue::VOBJ); uint256 hashBlock,hentropy; CTransaction vintx; uint64_t lockedfunds,sbits; int32_t ticketsize,odds,firstheight,period; CPubKey lottopk; struct CCcontract_info *cp,C; char str[67],numstr[65]; + if ( GetTransaction(lottoid,vintx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find lottoid\n"); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find lottoid")); + return(result); + } + if ( vintx.vout.size() > 0 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 0 ) + { + fprintf(stderr,"lottoid isnt lotto creation txid\n"); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","lottoid isnt lotto creation txid")); + return(result); + } + result.push_back(Pair("result","success")); + result.push_back(Pair("lottoid",uint256_str(str,lottoid))); + unstringbits(str,sbits); + result.push_back(Pair("name",str)); + result.push_back(Pair("sbits",sbits)); + result.push_back(Pair("ticketsize",ticketsize)); + result.push_back(Pair("odds",odds)); + cp = CCinit(&C,EVAL_LOTTO); + lottopk = GetUnspendable(cp,0); + lockedfunds = LottoPlanFunds(sbits,cp,lottopk,lottoid); + sprintf(numstr,"%.8f",(double)lockedfunds/COIN); + result.push_back(Pair("jackpot",numstr)); + return(result); +} + +UniValue LottoList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,hentropy; CTransaction vintx; uint64_t sbits; int32_t ticketsize,odds,firstheight,period; char str[65]; + cp = CCinit(&C,EVAL_LOTTO); + 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 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 'F' ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + +std::string LottoCreate(uint64_t txfee,char *planstr,int64_t funding,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period) +{ + CMutableTransaction mtx; uint256 entropy,hentropy; CPubKey mypk,lottopk; uint64_t sbits,int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_LOTTO); if ( txfee == 0 ) txfee = 10000; - Lottopk = GetUnspendable(cp,0); + lottopk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddLottoInputs(cp,mtx,Lottopk,nValue+txfee,60)) > 0 ) + sbits = stringbits(planstr); + if ( AddNormalinputs(mtx,mypk,funding+txfee,60) > 0 ) + { + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,funding,lottopk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_LOTTO << (uint8_t)'F' << sbits << ticketsize << odds << firstheight << period << hentropy))); + } +} + +std::string LottoTicket(uint64_t txfee,uint256 lottoid,int64_t numtickets) +{ + CMutableTransaction mtx; CPubKey mypk,lottopk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_LOTTO); + if ( txfee == 0 ) + txfee = 10000; + lottopk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( (inputs= AddLottoInputs(cp,mtx,lottopk,nValue+txfee,60)) > 0 ) { if ( inputs > nValue ) CCchange = (inputs - nValue - txfee); if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,CCchange,Lottopk)); + mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,CCchange,lottopk)); 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 Lotto inputs\n"); @@ -169,15 +319,15 @@ std::string LottoTicket(uint64_t txfee,int64_t numtickets) std::string LottoWinner(uint64_t txfee) { - CMutableTransaction mtx; CPubKey mypk,Lottopk; int64_t winnings = 0; CScript opret; struct CCcontract_info *cp,C; + CMutableTransaction mtx; CPubKey mypk,lottopk; int64_t winnings = 0; CScript opret; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_LOTTO); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - Lottopk = GetUnspendable(cp,0); + lottopk = GetUnspendable(cp,0); if ( AddNormalinputs(mtx,mypk,txfee,64) > 0 ) { - mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,winnings,Lottopk)); + mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,winnings,lottopk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } return(""); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 20ece62d2..a597b1664 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -755,6 +755,7 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim if ( height > 1 && checktoshis == 0 ) { checktoshis = ((uint64_t)GetBlockSubsidy(height, Params().GetConsensus()) - block.vtx[0].vout[0].nValue); + // some pools will need to change their pool fee to be (poolfee % - txfees) //checktoshis += txn_count * 0.001; // rely on higher level validations to prevent emitting more coins than actual txfees } if ( height >= 2 && (overflow != 0 || total > checktoshis || strangeout != 0) ) diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 2d64bc8fc..918e7c4f6 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -46,7 +46,7 @@ struct komodo_state KOMODO_STATES[34]; int COINBASE_MATURITY = _COINBASE_MATURITY;//100; int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,KOMODO_CONNECTING = -1; -int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,JUMBLR_PAUSE = 1; +int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 616f7ecaa..3a380c07c 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1544,6 +1544,7 @@ void komodo_args(char *argv0) } KOMODO_STOPAT = GetArg("-stopat",0); ASSETCHAINS_CC = GetArg("-ac_cc",0); + KOMODO_CCACTIVATE = GetArg("-ac_ccactivate",0); ASSETCHAINS_PUBLIC = GetArg("-ac_public",0); ASSETCHAINS_PRIVATE = GetArg("-ac_private",0); if ( (KOMODO_REWIND= GetArg("-rewind",0)) != 0 ) @@ -1650,6 +1651,8 @@ void komodo_args(char *argv0) //printf("created (%s)\n",fname); } else printf("error creating (%s)\n",fname); #endif + if ( KOMODO_CCACTIVATE != 0 && ASSETCHAINS_CC == 0 ) + ASSETCHAINS_CC = 2; } else { diff --git a/src/main.h b/src/main.h index dfea318cb..fd418502a 100644 --- a/src/main.h +++ b/src/main.h @@ -104,8 +104,8 @@ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; //static const bool DEFAULT_ADDRESSINDEX = false; //static const bool DEFAULT_SPENTINDEX = false; -#define DEFAULT_ADDRESSINDEX (GetArg("-ac_cc",0) != 0) -#define DEFAULT_SPENTINDEX (GetArg("-ac_cc",0) != 0) +#define DEFAULT_ADDRESSINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) +#define DEFAULT_SPENTINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) static const bool DEFAULT_TIMESTAMPINDEX = false; static const unsigned int DEFAULT_DB_MAX_OPEN_FILES = 1000; static const bool DEFAULT_DB_COMPRESSION = true; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 309708c7f..84ab1827c 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -73,8 +73,10 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector hashBytes; uint160 x; int32_t i; uint8_t hash20[20],*ptr;; x = Hash160(scriptPubKey);