From db25c36f2d7c26c90fa90816cc88c1a982396eb2 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 15:20:14 +0300 Subject: [PATCH 01/12] v0.5.0 port (part 1) port of https://github.com/KomodoPlatform/komodo/commit/b2c7a896856359674e6efcb2e2c29ed79e8e73eb to komodo-qt --- src/chain.h | 18 +- src/komodo_bitcoind.h | 393 ++++++++++++++++++++++++--------------- src/komodo_defs.h | 96 +++++++++- src/komodo_globals.h | 3 + src/komodo_notary.h | 4 +- src/komodo_utils.h | 1 + src/main.cpp | 286 ++++++++++++++-------------- src/main.h | 5 +- src/miner.cpp | 150 ++++++++++----- src/net.h | 3 - src/notaries_staked.cpp | 5 +- src/pow.cpp | 10 +- src/rpc/mining.cpp | 13 +- src/version.h | 2 +- src/wallet/rpcwallet.cpp | 11 +- src/wallet/walletdb.cpp | 2 +- 16 files changed, 629 insertions(+), 373 deletions(-) diff --git a/src/chain.h b/src/chain.h index a571397a..08cbdc7b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -28,7 +28,6 @@ class CChainPower; #include "pow.h" #include "tinyformat.h" #include "uint256.h" -extern int8_t is_STAKED(const char *chain_name); #include @@ -39,6 +38,10 @@ static const int SAPLING_VALUE_VERSION = 1010100; extern int32_t ASSETCHAINS_LWMAPOS; extern char ASSETCHAINS_SYMBOL[65]; extern uint64_t ASSETCHAINS_NOTARY_PAY[]; +extern int32_t ASSETCHAINS_STAKED; +extern const uint32_t nStakedDecemberHardforkTimestamp; //December 2019 hardfork +extern const int32_t nDecemberHardforkHeight; //December 2019 hardfork +extern int8_t is_STAKED(const char *chain_name); struct CDiskBlockPos { @@ -547,11 +550,22 @@ class CDiskBlockIndex : public CBlockIndex if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) { READWRITE(nSaplingValue); } - if ( (s.GetType() & SER_DISK) && (is_STAKED(ASSETCHAINS_SYMBOL) != 0) && ASSETCHAINS_NOTARY_PAY[0] != 0 ) + + // leave the existing LABS exemption here for segid and notary pay, but also add a timestamp activated segid for non LABS PoS64 chains. + if ( (s.GetType() & SER_DISK) && is_STAKED(ASSETCHAINS_SYMBOL) != 0 && ASSETCHAINS_NOTARY_PAY[0] != 0 ) { READWRITE(nNotaryPay); + } + if ( (s.GetType() & SER_DISK) && ASSETCHAINS_STAKED != 0 && (nTime > nStakedDecemberHardforkTimestamp || is_STAKED(ASSETCHAINS_SYMBOL) != 0) ) //December 2019 hardfork + { READWRITE(segid); } + + /*if ( (s.GetType() & SER_DISK) && (is_STAKED(ASSETCHAINS_SYMBOL) != 0) && ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + READWRITE(nNotaryPay); + READWRITE(segid); + }*/ } uint256 GetBlockHash() const diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 7517c39f..2d9b1bbe 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -33,6 +33,7 @@ extern CScript KOMODO_EARLYTXID_SCRIPTPUB; int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); +uint32_t komodo_heightstamp(int32_t height); //#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr)) @@ -548,6 +549,18 @@ int32_t komodo_verifynotarization(char *symbol,char *dest,int32_t height,int32_t return(retval); } +CScript komodo_makeopret(CBlock *pblock, bool fNew) +{ + std::vector vLeaves; + vLeaves.push_back(pblock->hashPrevBlock); + for (int32_t i = 0; i < pblock->vtx.size()-(fNew ? 0 : 1); i++) + vLeaves.push_back(pblock->vtx[i].GetHash()); + uint256 merkleroot = GetMerkleRoot(vLeaves); + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << merkleroot); + return(opret); +} + /*uint256 komodo_getblockhash(int32_t height) { uint256 hash; char params[128],*hexstr,*jsonstr; cJSON *result; int32_t i; uint8_t revbuf[32]; @@ -663,82 +676,114 @@ uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) return(txtime); } -int32_t komodo_WhoStaked(CBlock *pblock, CTxDestination &addressout) +CScript EncodeStakingOpRet(uint256 merkleroot) { - int32_t n,vout; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; - if ( (n= pblock->vtx.size()) > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1 ) + CScript opret; uint8_t evalcode = 77; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << merkleroot); + return(opret); +} + +uint8_t DecodeStakingOpRet(CScript scriptPubKey, uint256 &merkleroot) +{ + std::vector vopret; uint8_t evalcode; + GetOpReturnData(scriptPubKey, vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> evalcode; ss >> merkleroot) != 0 && evalcode == 77 ) { - txid = pblock->vtx[n-1].vin[0].prevout.hash; - vout = pblock->vtx[n-1].vin[0].prevout.n; - txtime = komodo_txtime(opret,&value,txid,vout,destaddr); - if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) - { - strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - if ( strcmp(destaddr,voutaddr) == 0 && pblock->vtx[n-1].vout[0].nValue == value ) - { - //LogPrintf("is PoS block!\n"); - addressout = voutaddress; - return(1); - } - } + //LogPrintf( "evalcode.%i merkleroot.%s\n",evalcode, merkleroot.GetHex().c_str() ); + return(1); } return(0); } -bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx); +int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp) +{ + if ( timestamp > nStakedDecemberHardforkTimestamp || komodo_heightstamp(height) > nStakedDecemberHardforkTimestamp ) //December 2019 hardfork + return(1); + else return(0); +} -int32_t komodo_isPoS2(CBlock *pblock) +int32_t komodo_hasOpRet(int32_t height, uint32_t timestamp) +{ + return((ASSETCHAINS_MARMARA!=0 || komodo_newStakerActive(height, timestamp) == 1)); +} + +bool komodo_checkopret(CBlock *pblock, CScript &merkleroot) { - CBlockIndex *pindex = komodo_blockindex(pblock->GetHash()); - if ( pindex != 0 && pindex->segid >= -1 ) - { - //LogPrintf("isPoSblock segid.%d\n",pindex->segid); - if ( pindex->segid == -1 ) - return(0); - else return(1); - } - return (-1); + merkleroot = pblock->vtx.back().vout.back().scriptPubKey; + return(merkleroot.IsOpReturn() && merkleroot == komodo_makeopret(pblock, false)); } -int32_t komodo_isPoS(CBlock *pblock,int32_t height,bool fJustCheck) +bool komodo_hardfork_active(uint32_t time) { - int32_t n,vout,numvouts,ret; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; + return ( (ASSETCHAINS_SYMBOL[0] == 0 && chainActive.Height() > nDecemberHardforkHeight) || (ASSETCHAINS_SYMBOL[0] != 0 && time > nStakedDecemberHardforkTimestamp) ); //December 2019 hardfork +} + +bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx); + +uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHeight, bool fNew, CScript scriptPubKey) +{ + std::vector vLeaves; + // rereate coinbase tx + CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight); + txNew.vin.resize(1); + txNew.vin[0].prevout.SetNull(); + txNew.vin[0].scriptSig = fNew ? (CScript() << nHeight << CScriptNum(1)) + COINBASE_FLAGS : pblock->vtx[0].vin[0].scriptSig; + txNew.vout.resize(1); + txNew.vout[0].scriptPubKey = scriptPubKey; + txNew.vout[0].nValue = 0; + txNew.nExpiryHeight = 0; + txNew.nLockTime = 0; + //LogPrintf( "validation: coinbasetx.%s\n", EncodeHexTx(txNew).c_str()); + //LogPrintf( "txnew.%s\n", txNew.GetHash().ToString().c_str()); + vLeaves.push_back(txNew.GetHash()); + vLeaves.push_back(prevBlockHash); + for (int32_t i = 1; i < pblock->vtx.size()-(fNew ? 0 : 1); i++) + vLeaves.push_back(pblock->vtx[i].GetHash()); + return GetMerkleRoot(vLeaves); +} + +int32_t komodo_isPoS(CBlock *pblock, int32_t height,CTxDestination *addressout) +{ + int32_t n,vout,numvouts,ret; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid, merkleroot; CScript opret; if ( ASSETCHAINS_STAKED != 0 ) { - if ( fJustCheck ) - { - // check pindex first, if that does not work, continue with slow check. - if ( (ret= komodo_isPoS2(pblock)) == 1 ) - return (1); - else if ( ret == 0 ) - return (0); - } n = pblock->vtx.size(); //LogPrintf("ht.%d check for PoS numtx.%d numvins.%d numvouts.%d\n",height,n,(int32_t)pblock->vtx[n-1].vin.size(),(int32_t)pblock->vtx[n-1].vout.size()); - if ( n > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1+(ASSETCHAINS_MARMARA!=0) ) + if ( n > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1+komodo_hasOpRet(height,pblock->nTime) ) { txid = pblock->vtx[n-1].vin[0].prevout.hash; vout = pblock->vtx[n-1].vin[0].prevout.n; txtime = komodo_txtime(opret,&value,txid,vout,destaddr); if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) { + if ( addressout != 0 ) *addressout = voutaddress; strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); //LogPrintf("voutaddr.%s vs destaddr.%s\n",voutaddr,destaddr); - if ( pblock->vtx[n-1].vout[0].nValue == value && strcmp(destaddr,voutaddr) == 0 ) + if ( komodo_newStakerActive(height, pblock->nTime) != 0 ) { - if ( ASSETCHAINS_MARMARA == 0 ) + if ( DecodeStakingOpRet(pblock->vtx[n-1].vout[1].scriptPubKey, merkleroot) != 0 && komodo_calcmerkleroot(pblock, pblock->hashPrevBlock, height, false, pblock->vtx[0].vout[0].scriptPubKey) == merkleroot ) + { return(1); - else + } + } + else + { + if ( pblock->vtx[n-1].vout[0].nValue == value && strcmp(destaddr,voutaddr) == 0 ) { - if ( pblock->vtx[n-1].vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= pblock->vtx[n-1].vout.size()) == 2 ) + if ( ASSETCHAINS_MARMARA == 0 ) + return(1); + else { -//LogPrintf("validate proper %s %s signature and unlockht preservation\n",voutaddr,destaddr); - return(MarmaraPoScheck(destaddr,opret,pblock->vtx[n-1])); - } - else - { - LogPrintf("reject ht.%d PoS block\n",height); - return(strcmp(ASSETCHAINS_SYMBOL,"MTST2") == 0); // allow until MTST3 + if ( pblock->vtx[n-1].vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= pblock->vtx[n-1].vout.size()) == 2 ) + { + //LogPrintf("validate proper %s %s signature and unlockht preservation\n",voutaddr,destaddr); + return(MarmaraPoScheck(destaddr,opret,pblock->vtx[n-1])); + } + else + { + LogPrintf("reject ht.%d PoS block\n",height); + return(strcmp(ASSETCHAINS_SYMBOL,"MTST2") == 0); // allow until MTST3 + } } } } @@ -996,6 +1041,7 @@ void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height, int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t blocktimes[66],int32_t *nonzpkeysp,int32_t height) { + // after the season HF block ALL new notaries instantly become elegible. int32_t i,j,n,duplicate; CBlock block; CBlockIndex *pindex; uint8_t notarypubs33[64][33]; memset(mids,-1,sizeof(*mids)*66); n = komodo_notaries(notarypubs33,height,0); @@ -1252,6 +1298,7 @@ int32_t komodo_isrealtime(int32_t *kmdheightp) int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t cmptime,int32_t dispflag) { + dispflag = 1; if ( KOMODO_REWIND == 0 && ASSETCHAINS_SYMBOL[0] == 0 && (int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD ) //1473793441 ) { if ( txheight > 246748 ) @@ -1383,15 +1430,16 @@ uint32_t komodo_segid32(char *coinaddr) int8_t komodo_segid(int32_t nocache,int32_t height) { - CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout; uint256 txid; CScript opret; int8_t segid = -1; + CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout,newStakerActive; uint256 txid,merkleroot; CScript opret; int8_t segid = -1; if ( height > 0 && (pindex= komodo_chainactive(height)) != 0 ) { if ( nocache == 0 && pindex->segid >= -1 ) return(pindex->segid); if ( komodo_blockload(block,pindex) == 0 ) { + newStakerActive = komodo_newStakerActive(height, block.nTime); txn_count = block.vtx.size(); - if ( txn_count > 1 && block.vtx[txn_count-1].vin.size() == 1 && block.vtx[txn_count-1].vout.size() == 1 ) + if ( txn_count > 1 && block.vtx[txn_count-1].vin.size() == 1 && block.vtx[txn_count-1].vout.size() == 1+komodo_hasOpRet(height,pindex->nTime) ) { txid = block.vtx[txn_count-1].vin[0].prevout.hash; vout = block.vtx[txn_count-1].vin[0].prevout.n; @@ -1399,15 +1447,20 @@ int8_t komodo_segid(int32_t nocache,int32_t height) if ( ExtractDestination(block.vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) ) { strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - if ( strcmp(destaddr,voutaddr) == 0 && block.vtx[txn_count-1].vout[0].nValue == value ) + if ( newStakerActive == 1 && block.vtx[txn_count-1].vout.size() == 2 && DecodeStakingOpRet(block.vtx[txn_count-1].vout[1].scriptPubKey, merkleroot) != 0 ) + newStakerActive++; + if ( newStakerActive == 2 || (newStakerActive == 0 && strcmp(destaddr,voutaddr) == 0 && block.vtx[txn_count-1].vout[0].nValue == value) ) { segid = komodo_segid32(voutaddr) & 0x3f; - pindex->segid = segid; - //LogPrintf("komodo_segid.(%d) -> %d\n",height,pindex->segid); + //LogPrintf( "komodo_segid: ht.%i --> %i\n",height,pindex->segid); } - } else LogPrintf("komodo_segid ht.%d couldnt extract voutaddress\n",height); + } //else LogPrintf("komodo_segid ht.%d couldnt extract voutaddress\n",height); } } + // The new staker sets segid in komodo_checkPOW, this persists after restart by being saved in the blockindex for blocks past the HF timestamp, to keep backwards compatibility. + // PoW blocks cannot contain a staking tx. If segid has not yet been set, we can set it here accurately. + if ( pindex->segid == -2 ) + pindex->segid = segid; } return(segid); } @@ -1423,7 +1476,7 @@ void komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n) memset(hashbuf,0xff,n); for (i=0; i easydiff ) + bnTarget = easydiff; return(bnTarget); } @@ -1587,7 +1649,8 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh if ( blocktime+iter+segid*2 < txtime+minage ) continue; diff = (iter + blocktime - txtime - minage); - if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + // Disable PoS64 on VerusHash, doesnt work properly. + if ( 0 ) // ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) { /*if ( PoSperc < ASSETCHAINS_STAKED ) { @@ -1615,7 +1678,7 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh if ( iter > 0 ) diff += segid*2; coinage = (value * diff); - if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + if ( 0 ) //&& ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) { if ( PoSperc < ASSETCHAINS_STAKED ) { @@ -1669,24 +1732,25 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_uint256 bnTarget,arith_uint256 bhash) { - CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t ret,vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; arith_uint256 POWTarget; + CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid, merkleroot; uint32_t txtime,prevtime=0; int32_t ret,vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; arith_uint256 POWTarget; if ( ASSETCHAINS_STAKED == 100 && height <= 10 ) return(1); BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); - pindex = it != mapBlockIndex.end() ? it->second : NULL; - if ( pindex != 0 && pindex->segid >= -1 ) - { - //LogPrintf("isPoSblock segid.%d\n",pindex->segid); - if ( pindex->segid == -1 ) - return(0); - else return(1); - } + pindex = it != mapBlockIndex.end() ? it->second : NULL; + int32_t newStakerActive = komodo_newStakerActive(height, pblock->nTime); // Get PoSperc and POW Target. slowflag only here, calling it when blocks out of order causes problems. if ( slowflag != 0 ) - POWTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); + { + POWTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED,newStakerActive); + } + else + { + // checks opret merkle root and existence of staking tx. + return(komodo_isPoS(pblock,height,0)); + } txn_count = pblock->vtx.size(); //LogPrintf("checkblock n.%d vins.%d vouts.%d %.8f %.8f\n",txn_count,(int32_t)pblock->vtx[txn_count-1].vin.size(),(int32_t)pblock->vtx[txn_count-1].vout.size(),(double)pblock->vtx[txn_count-1].vout[0].nValue/COIN,(double)pblock->vtx[txn_count-1].vout[1].nValue/COIN); - if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1 + (ASSETCHAINS_MARMARA!=0) ) + if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1+komodo_hasOpRet(height,pblock->nTime) ) { it = mapBlockIndex.find(pblock->hashPrevBlock); if ( it != mapBlockIndex.end() && (previndex = it->second) != NULL ) @@ -1696,58 +1760,32 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ vout = pblock->vtx[txn_count-1].vin[0].prevout.n; if ( slowflag != 0 && prevtime != 0 ) { - if ( komodo_isPoS(pblock,height,false) != 0 ) + if ( komodo_isPoS(pblock,height,0) != 0 ) { - eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)"",PoSperc); + // checks utxo is eligible to stake this block + eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF,(char *)"",PoSperc); } if ( eligible == 0 || eligible > pblock->nTime ) { - if ( 0 && ASSETCHAINS_STAKED < 100 ) + if ( 0 && ASSETCHAINS_STAKED < 100 ) LogPrintf("komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)pblock->nTime,(int32_t)(eligible - pblock->nTime)); - if ( pindex != 0 ) - { - pindex->segid = -1; - //LogPrintf("PoW block detected set segid.%d <- %d\n",height,pindex->segid); - } } - else + else { - isPoS = 2; // 2 means staking utxo validated - CTxDestination voutaddress; char voutaddr[64]; - if ( ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) ) - { - strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - segid = komodo_segid32(voutaddr) & 0x3f; - } - if ( pindex != 0 && segid >= 0 ) - { - pindex->segid = segid; - //LogPrintf("PoS block set segid.%d <- %d\n",height,pindex->segid); - } //else LogPrintf("unexpected null pindex for slowflag set ht.%d segid.%d:%d\n",height,pindex!=0?pindex->segid:-3,segid); - } - } - else if ( slowflag == 0 ) // previous blocks are not seen yet, do the best approx - { - if ( komodo_isPoS(pblock,height,false) != 0 ) isPoS = 1; - } - if ( slowflag != 0 && isPoS != 0 ) - { - if ( isPoS != 2 ) - { - LogPrintf("ht.%d isPoS.%d utxo not validated -> must be PoW fake\n",height,isPoS); - isPoS = 0; - } - else if ( ASSETCHAINS_STAKED != 100 ) - { - if ( bhash < POWTarget ) + /* + If POWTarget is easydiff, then we have no possible way to detect a PoW block from a staking block! + The simplest fix is to make the min diff for PoW blocks higher than the staking mindiff. + The logic here, is that all PoS equihash solutions MUST be under the POW target diff, + The floor diff can be adjusted with ASSETCHAINS_STAKED_MIN_POW_DIFF, this is a hardforking change. + */ + if ( ASSETCHAINS_STAKED < 100 && bhash < POWTarget ) { - //LogPrintf("ht.%d isPoS but meets PoW diff!\n",height); + LogPrintf("[%s:%i] isPoS but meets PoW diff nBits.%u < target.%u\n", ASSETCHAINS_SYMBOL, height, bhash.GetCompact(), POWTarget.GetCompact()); isPoS = 0; } } - } - //else return(-1); + } } //LogPrintf("slow.%d ht.%d isPoS.%d\n",slowflag,height,isPoS); return(isPoS != 0); @@ -2263,9 +2301,9 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height) bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0; -int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) +int32_t komodo_checkPOW(int64_t stakeTxValue, int32_t slowflag,CBlock *pblock,int32_t height) { - uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,scriptlen,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; + uint256 hash,merkleroot; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,scriptlen,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; if ( KOMODO_TEST_ASSETCHAIN_SKIP_POW == 0 && Params().NetworkIDString() == "regtest" ) KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1; if ( !CheckEquihashSolution(pblock, Params()) ) @@ -2324,16 +2362,20 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) //LogPrintf("ASSETCHAINS_STAKED.%d ht.%d\n",(int32_t)ASSETCHAINS_STAKED,height); if ( ASSETCHAINS_STAKED != 0 && height >= 2 ) // must PoS or have at least 16x better PoW { + CBlockIndex *pindex; + BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); + pindex = it != mapBlockIndex.end() ? it->second : NULL; + int32_t newStakerActive = komodo_newStakerActive(height, pblock->nTime); if ( (is_PoSblock= komodo_is_PoSblock(slowflag,height,pblock,bnTarget,bhash)) == 0 ) { - if ( slowflag == 0 ) // need all past 100 blocks to calculate PoW target + if ( slowflag == 0 || height <= 100 ) // need all past 100 blocks to calculate PoW target return(0); if ( ASSETCHAINS_STAKED == 100 && height > 100 ) // only PoS allowed! POSTEST64 return(-1); else { - bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); - if ( bhash > bnTarget && height > 100 ) + bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED,newStakerActive); + if ( bhash > bnTarget ) { for (i=31; i>=16; i--) LogPrintf("%02x",((uint8_t *)&bhash)[i]); @@ -2345,24 +2387,62 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) } else { - failed = 0; - CBlockIndex *pindex; - BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); - pindex = it != mapBlockIndex.end() ? it->second : NULL; - if ( pindex != 0 && pindex->segid == -2 ) { - pindex->segid = -1; - //LogPrintf("PoW block detected set segid.%d <- %d\n",height,pindex->segid); + if ( newStakerActive != 0 ) + { + // PoW fake blocks will be rejected here. If a staking tx is included in a block that meets PoW min diff after block 100, then this will reject it. + if ( pblock->vtx.size() > 1 && pblock->vtx[pblock->vtx.size()-1].vout.size() == 2 && DecodeStakingOpRet(pblock->vtx[pblock->vtx.size()-1].vout[1].scriptPubKey, merkleroot) != 0 ) + { + LogPrintf( "[%s:%d] staking tx in PoW block, nBits.%u < target.%u\n", ASSETCHAINS_SYMBOL,height,bhash.GetCompact(),bnTarget.GetCompact()); + return(-1); + } + // set the pindex->segid as this is now fully validated to be a PoW block. + if ( pindex != 0 ) + { + pindex->segid = -1; + //LogPrintf("PoW block detected set segid.%d <- %d\n",height,pindex->segid); + } } + failed = 0; } } } else if ( is_PoSblock < 0 ) { - LogPrintf("unexpected negative is_PoSblock.%d\n",is_PoSblock); + LogPrintf("[%s:%d] unexpected negative is_PoSblock.%d\n",ASSETCHAINS_SYMBOL,height,is_PoSblock); return(-1); } - else if ( ASSETCHAINS_STAKED != 0 ) + else + { + if ( slowflag != 0 && newStakerActive != 0 ) + { + int8_t segid = -2; + // the value passed to stakeTxValue, is the blockreward + the valuein-valueout(txfee) of the last tx in the block. + // the coinbase must pay the fees from the last transaction and the block reward at a minimum. + if ( pblock->vtx.size() < 1 || pblock->vtx[0].vout.size() < 1 ) + { + LogPrintf( "[%s:%d] missing coinbase.\n", ASSETCHAINS_SYMBOL, height); + return(-1); + } + else if ( pblock->vtx[0].vout[0].nValue < stakeTxValue ) + { + LogPrintf( "[%s:%d] coinbase vout0.%lld < blockreward+stakingtxfee.%lld\n", ASSETCHAINS_SYMBOL, height, (long long)pblock->vtx[0].vout[0].nValue, (long long)stakeTxValue); + return(-1); + } + // set the pindex->segid as this is now fully validated to be a PoS block. + char voutaddr[64]; CTxDestination voutaddress; + if ( ExtractDestination(pblock->vtx.back().vout[0].scriptPubKey,voutaddress) ) + { + strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); + segid = komodo_segid32(voutaddr) & 0x3f; + } + if ( pindex != 0 && segid >= 0 ) + { + pindex->segid = segid; + //LogPrintf("PoS block set segid.%d <- %d\n",height,pindex->segid); + } + } failed = 0; + } } if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) { @@ -2398,10 +2478,10 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) // the default daemon miner, checks the actual vins so the only way this will fail, is if someone changes the miner, // and then creates txs to the crypto address meeting min sigs and puts it in tx position 1. // If they go through this effort, the block will still fail at connect block, and will be auto purged by the temp file fix. - if ( failed == 0 && ASSETCHAINS_NOTARY_PAY[0] != 0 && pblock->vtx[0].vout.size() > 1 ) + if ( failed == 0 && ASSETCHAINS_NOTARY_PAY[0] != 0 && pblock->vtx.size() > 1 ) { // We check the full validation in ConnectBlock directly to get the amount for coinbase. So just approx here. - if ( slowflag == 0 ) + if ( slowflag == 0 && pblock->vtx[0].vout.size() > 1 ) { // Check the notarisation tx is to the crypto address. if ( !komodo_is_notarytx(pblock->vtx[1]) == 1 ) @@ -2563,11 +2643,12 @@ struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numk return(array); } -int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig) +int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig, uint256 merkleroot) { static struct komodo_staking *array; static int32_t numkp,maxkp; static uint32_t lasttime; - int32_t PoSperc; + int32_t PoSperc = 0, newStakerActive; set setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget,tmpTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock; + uint64_t cbPerc = *utxovaluep, tocoinbase = 0; if (!EnsureWalletIsAvailable(0)) return 0; @@ -2580,25 +2661,25 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt if ( (tipindex= chainActive.Tip()) == 0 ) return(0); nHeight = tipindex->GetHeight() + 1; - // Get the PoS% so we can pass it to komodo_stake, this is to adjust PoS dofficulty when it is under the target %! - tmpTarget = komodo_PoWtarget(&PoSperc,bnTarget,nHeight,ASSETCHAINS_STAKED); if ( (minage= nHeight*3) > 6000 ) // about 100 blocks minage = 6000; - komodo_segids(hashbuf,nHeight-101,100); - if ( *blocktimep < tipindex->nTime+60) + if ( *blocktimep < tipindex->nTime+60 ) *blocktimep = tipindex->nTime+60; - + komodo_segids(hashbuf,nHeight-101,100); + // this was for VerusHash PoS64 + //tmpTarget = komodo_PoWtarget(&PoSperc,bnTarget,nHeight,ASSETCHAINS_STAKED); bool resetstaker = false; if ( array != 0 ) { + LOCK(cs_main); CBlockIndex* pblockindex = chainActive[tipindex->GetHeight()]; CBlock block; CTxDestination addressout; if ( ASSETCHAINS_MARMARA != 0 ) resetstaker = true; - else if( ReadBlockFromDisk(block, pblockindex, 1) && komodo_WhoStaked(&block, addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) + else if ( ReadBlockFromDisk(block, pblockindex, 1) && komodo_isPoS(&block, nHeight, &addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) { resetstaker = true; - LogPrintf( "Reset ram staker after mining a block!\n"); + LogPrintf( "[%s:%d] Reset ram staker after mining a block!\n",ASSETCHAINS_SYMBOL,nHeight); } } @@ -2619,7 +2700,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt { if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight ) { - LogPrintf("chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); + LogPrintf("[%s:%d] chain tip changed during staking loop t.%u counter.%d\n",ASSETCHAINS_SYMBOL,nHeight,(uint32_t)time(NULL),counter); return(0); } counter++; @@ -2636,7 +2717,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt { if ( IsMine(*pwalletMain,address) == 0 ) continue; - if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 ) + if ( myGetTransaction(out.tx->GetHash(),tx,hashBlock) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 ) { array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk); //LogPrintf("addutxo numkp.%d vs max.%d\n",numkp,maxkp); @@ -2659,7 +2740,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt vout = (int32_t)it->first.index; if ( (nValue= it->second.satoshis) < COIN ) continue; - if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { const CScript &scriptPubKey = tx.vout[vout].scriptPubKey; if ( DecodeMaramaraCoinbaseOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,pk,ht,unlockht) != 0 && pk == mypk ) @@ -2673,23 +2754,22 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt lasttime = (uint32_t)time(NULL); //LogPrintf("finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); } - block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57; + block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX; for (i=winners=0; iGetHeight()+1 > nHeight ) { - LogPrintf("chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); + LogPrintf("[%s:%d] chain tip changed during staking loop t.%u counter.%d\n",ASSETCHAINS_SYMBOL,nHeight,(uint32_t)time(NULL),i); return(0); } kp = &array[i]; - //kp = array + i; - eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address,PoSperc); + eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF,kp->address,PoSperc); if ( eligible > 0 ) { besttime = 0; - if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) ) + if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF,kp->address,PoSperc) ) { // have elegible utxo to stake with. if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || kp->nValue < *utxovaluep)) ) @@ -2702,9 +2782,13 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt *utxovoutp = kp->vout; *txtimep = kp->txtime; } - if ( eligible < block_from_future_rejecttime ) // nothing gained by going earlier + /*if ( eligible < block_from_future_rejecttime ) + { + // better to scan all and choose earliest! + LogPrintf( "block_from_future_rejecttime.%u vs eligible.%u \n", block_from_future_rejecttime, eligible); break; - } else continue; + } */ + } } } if ( numkp < 500 && array != 0 ) @@ -2729,6 +2813,18 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt txNew.vout[0].scriptPubKey = best_scriptPubKey; txNew.vout[0].nValue = *utxovaluep - txfee; txNew.nLockTime = earliest; + txNew.nExpiryHeight = nHeight; + if ( (newStakerActive= komodo_newStakerActive(nHeight,earliest)) != 0 ) + { + if ( cbPerc > 0 && cbPerc <= 100 ) + { + tocoinbase = txNew.vout[0].nValue*cbPerc/100; + txNew.vout[0].nValue -= tocoinbase; + } + txNew.vout.resize(2); + txNew.vout[1].scriptPubKey = EncodeStakingOpRet(merkleroot); + txNew.vout[1].nValue = 0; + } CTransaction txNewConst(txNew); if ( ASSETCHAINS_MARMARA == 0 ) { @@ -2738,6 +2834,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt siglen = sigdata.scriptSig.size(); for (i=0; i vWhiteListAddress; extern std::map mapHeightEvalActivate; void komodo_netevent(std::vector payload); int32_t getacseason(uint32_t timestamp); +int32_t getkmdseason(int32_t height); #define IGUANA_MAXSCRIPTSIZE 10001 #define KOMODO_KVDURATION 1440 @@ -336,6 +413,7 @@ int64_t komodo_pricemult(int32_t ind); int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); int32_t komodo_currentheight(); - - +bool komodo_hardfork_active(uint32_t time); +int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); +uint32_t komodo_chainactive_timestamp(); #endif diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 29dd33fc..2c4d241a 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -41,6 +41,8 @@ int32_t NUM_PRICES; uint32_t *PVALS; struct knotaries_entry *Pubkeys; struct komodo_state KOMODO_STATES[34]; +const uint32_t nStakedDecemberHardforkTimestamp = 1576840000; //December 2019 hardfork 12/20/2019 @ 11:06am (UTC) +const int32_t nDecemberHardforkHeight = 1670000; //December 2019 hardfork #define _COINBASE_MATURITY 100 int COINBASE_MATURITY = _COINBASE_MATURITY;//100; @@ -114,6 +116,7 @@ unsigned int MAX_BLOCK_SIGOPS = 20000; int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; CScript KOMODO_EARLYTXID_SCRIPTPUB; int32_t ASSETCHAINS_EARLYTXIDCONTRACT; +int32_t ASSETCHAINS_STAKED_SPLIT_PERCENTAGE; std::map mapHeightEvalActivate; diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 57c4d9a4..235b6150 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -70,7 +70,7 @@ int32_t getkmdseason(int32_t height) return(1); for (int32_t i = 1; i < NUM_KMD_SEASONS; i++) { - if ( height <= KMD_SEASON_HEIGHTS[i] && height >= KMD_SEASON_HEIGHTS[i-1] ) + if ( height <= KMD_SEASON_HEIGHTS[i] && height > KMD_SEASON_HEIGHTS[i-1] ) return(i+1); } return(0); @@ -82,7 +82,7 @@ int32_t getacseason(uint32_t timestamp) return(1); for (int32_t i = 1; i < NUM_KMD_SEASONS; i++) { - if ( timestamp <= KMD_SEASON_TIMESTAMPS[i] && timestamp >= KMD_SEASON_TIMESTAMPS[i-1] ) + if ( timestamp <= KMD_SEASON_TIMESTAMPS[i] && timestamp > KMD_SEASON_TIMESTAMPS[i-1] ) return(i+1); } return(0); diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 1ca106f3..65f41f84 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1709,6 +1709,7 @@ void komodo_args(char *argv0) NOTARY_PUBKEY = GetArg("-pubkey", ""); KOMODO_DEALERNODE = GetArg("-dealer",0); KOMODO_TESTNODE = GetArg("-testnode",0); + ASSETCHAINS_STAKED_SPLIT_PERCENTAGE = GetArg("-splitperc",0); if ( strlen(NOTARY_PUBKEY.c_str()) == 66 ) { decode_hex(NOTARY_PUBKEY33,33,(char *)NOTARY_PUBKEY.c_str()); diff --git a/src/main.cpp b/src/main.cpp index e41b1f05..869d090a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -936,20 +936,34 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { - int32_t i; - if (tx.nLockTime == 0) + if (tx.nLockTime == 0) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - if ( txin.nSequence == 0xfffffffe && (((int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockTime) || ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockHeight)) ) + if ( !komodo_hardfork_active(nBlockTime) && txin.nSequence == 0xfffffffe && + //if ( (nBlockTime <= ASSETCHAINS_STAKED_HF_TIMESTAMP ) && txin.nSequence == 0xfffffffe && + ( + ((int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockTime) || + ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockHeight) + ) + ) + { + + } + //else if ( nBlockTime > ASSETCHAINS_STAKED_HF_TIMESTAMP && txin.nSequence == 0xfffffffe && + else if ( komodo_hardfork_active(nBlockTime) && txin.nSequence == 0xfffffffe && + ( + ((int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime <= nBlockTime) || + ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime <= nBlockHeight)) + ) { } else if (!txin.IsFinal()) { - //LogPrintf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); + LogPrintf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); return false; } } @@ -1357,9 +1371,9 @@ bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const prevind } bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState &state, - libzcash::ProofVerifier& verifier) + libzcash::ProofVerifier& verifier,int32_t txIndex, int32_t numTxs) { - static uint256 array[64]; static int32_t numbanned,indallvouts; int32_t j,k,n; + static uint256 array[64]; static int32_t numbanned,indallvouts; int32_t j,k,n; uint256 merkleroot; if ( *(int32_t *)&array[0] == 0 ) numbanned = komodo_bannedset(&indallvouts,array,(int32_t)(sizeof(array)/sizeof(*array))); n = tx.vin.size(); @@ -1379,6 +1393,17 @@ bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState } } } + + + if ( ASSETCHAINS_STAKED != 0 && komodo_newStakerActive(0, tiptime) != 0 && tx.vout.size() == 2 && DecodeStakingOpRet(tx.vout[1].scriptPubKey, merkleroot) != 0 ) + { + if ( numTxs == 0 || txIndex != numTxs-1 ) + { + return state.DoS(100, error("CheckTransaction(): staking tx is not staking a block"), + REJECT_INVALID, "bad-txns-stakingtx"); + } + } + // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { transactionsValidated.increment(); @@ -1756,7 +1781,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel, bool fSkipExpiry) +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1767,15 +1792,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if ( nextBlockHeight <= 1 || chainActive.LastTip() == 0 ) tiptime = (uint32_t)time(NULL); else tiptime = (uint32_t)chainActive.LastTip()->nTime; - - // is it already in the memory pool? - uint256 hash = tx.GetHash(); - if (pool.exists(hash)) - { - //LogPrintf("already in mempool\n"); - return state.Invalid(false, REJECT_DUPLICATE, "already in mempool"); - } - +//LogPrintf("addmempool 0\n"); // Node operator can choose to reject tx by number of transparent inputs static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "size_t too small"); size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0); @@ -1789,20 +1806,22 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; } } - +//LogPrintf("addmempool 1\n"); auto verifier = libzcash::ProofVerifier::Strict(); if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 ) { - //LogPrintf("AcceptToMemoryPool komodo_validate_interest failure\n"); + LogPrintf("AcceptToMemoryPool komodo_validate_interest failure\n"); return error("AcceptToMemoryPool: komodo_validate_interest failed"); } - if (!CheckTransaction(tiptime,tx, state, verifier)) + + if (!CheckTransaction(tiptime,tx, state, verifier, 0, 0)) { return error("AcceptToMemoryPool: CheckTransaction failed"); } + // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!fSkipExpiry && !ContextualCheckTransaction(0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) + if (!ContextualCheckTransaction(0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -1812,25 +1831,34 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LogPrintf("AcceptToMemoryPool coinbase as individual tx\n"); return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),REJECT_INVALID, "coinbase"); } + // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; - if (!fSkipExpiry && Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) + if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) { // //LogPrintf("AcceptToMemoryPool reject nonstandard transaction: %s\nscriptPubKey: %s\n",reason.c_str(),tx.vout[0].scriptPubKey.ToString().c_str()); return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason),REJECT_NONSTANDARD, reason); } + // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. - if (!fSkipExpiry && !CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) { //LogPrintf("AcceptToMemoryPool reject non-final\n"); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } +//LogPrintf("addmempool 3\n"); + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + if (pool.exists(hash)) + { + //LogPrintf("already in mempool\n"); + return state.Invalid(false, REJECT_DUPLICATE, "already in mempool"); + } // Check for conflicts with in-memory transactions - if (!fSkipExpiry) { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1856,7 +1884,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - +//LogPrintf("addmempool 4\n"); { CCoinsView dummy; CCoinsViewCache view(&dummy); @@ -1880,7 +1908,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (ExistsImportTombstone(tx, view)) return state.Invalid(false, REJECT_DUPLICATE, "import tombstone exists"); } - else if (!fSkipExpiry) + else { // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), @@ -1892,10 +1920,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pfMissingInputs) *pfMissingInputs = true; //LogPrintf("missing inputs\n"); - return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing"); + return false; + /* + https://github.com/zcash/zcash/blob/master/src/main.cpp#L1490 + state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing"); + */ } } - // are the actual inputs available? if (!view.HaveInputs(tx)) { @@ -1903,26 +1934,27 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); } } + // are the joinsplit's requirements met? if (!view.HaveJoinSplitRequirements(tx)) { //LogPrintf("accept failure.2\n"); return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); } + // Bring the best block into scope view.GetBestBlock(); - + nValueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime); if ( 0 && interest != 0 ) LogPrintf("add interest %.8f\n",(double)interest/COIN); // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); } - // Check for non-standard pay-to-script-hash in inputs if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) return error("AcceptToMemoryPool: reject nonstandard transaction input"); - + // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than @@ -1935,7 +1967,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LogPrintf("accept failure.4\n"); return state.DoS(1, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); } - + CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); @@ -1945,7 +1977,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; - if (!fSkipExpiry && !tx.IsCoinImport()) { + if (!tx.IsCoinImport()) { BOOST_FOREACH(const CTxIn &txin, tx.vin) { const CCoins *coins = view.AccessCoins(txin.prevout.hash); if (coins->IsCoinBase()) { @@ -1954,15 +1986,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - +//LogPrintf("addmempool 5\n"); // Grab the branch ID we expect this transaction to commit to. We don't // yet know if it does, but if the entry gets added to the mempool, then // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); - + // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. if (tx.vjoinsplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. @@ -1975,13 +2007,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee"); } } - + // Require that free transactions have sufficient priority to be mined in the next block. if (GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { LogPrintf("accept failure.6\n"); return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } - + // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. @@ -2007,7 +2039,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } - + if (!tx.IsCoinImport() && fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19) { string errmsg = strprintf("absurdly high fees %s, %d > %d", @@ -2020,12 +2052,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); - if (!fSkipExpiry && !ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) + if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { //LogPrintf("accept failure.9\n"); return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); } - + // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For @@ -2041,7 +2073,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa flag = 1; KOMODO_CONNECTING = (1<<30) + (int32_t)chainActive.LastTip()->GetHeight() + 1; } - if (!fSkipExpiry && !ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) +//LogPrintf("addmempool 7\n"); + + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { if ( flag != 0 ) KOMODO_CONNECTING = -1; @@ -2050,23 +2084,36 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if ( flag != 0 ) KOMODO_CONNECTING = -1; - // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); - - if (!tx.IsCoinImport()) { - // Add memory address index - if (fAddressIndex) { - pool.addAddressIndex(entry, view); - } + LOCK(pool.cs); + // Store transaction in memory + pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); + if (!tx.IsCoinImport()) + { + // Add memory address index + if (fAddressIndex) { + pool.addAddressIndex(entry, view); + } - // Add memory spent index - if (fSpentIndex) { - pool.addSpentIndex(entry, view); + // Add memory spent index + if (fSpentIndex) { + pool.addSpentIndex(entry, view); + } } } } + // This should be here still? + //SyncWithWallets(tx, NULL); + return true; +} +bool CCTxFixAcceptToMemPoolUnchecked(CTxMemPool& pool, const CTransaction &tx) +{ + // called from CheckBlock which is in cs_main and mempool.cs locks already. + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + CTxMemPoolEntry entry(tx, 0, GetTime(), 0, chainActive.Height(), mempool.HasNoInputsOf(tx), false, consensusBranchId); + //LogPrintf( "adding %s to mempool from block %d\n",tx.GetHash().ToString().c_str(),chainActive.GetHeight()); + pool.addUnchecked(tx.GetHash(), entry, false); return true; } @@ -2133,59 +2180,6 @@ struct CompareBlocksByHeightMain } }; -bool RemoveOrphanedBlocks(int32_t notarized_height) -{ - LOCK(cs_main); - std::vector prunedblocks; - std::set setTips; - int32_t m=0,n = 0; - // get notarised timestamp and use this as a backup incase the forked block has no height. - // we -600 to make sure the time is within future block constraints. - uint32_t notarized_timestamp = komodo_heightstamp(notarized_height)-600; - // Most of this code is a direct copy from GetChainTips RPC. Which gives a return of all - // blocks that are not in the main chain. - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - n++; - setTips.insert(item.second); - } - n = 0; - BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) - { - const CBlockIndex* pprev=0; - n++; - if ( item.second != 0 ) - pprev = item.second->pprev; - if (pprev) - setTips.erase(pprev); - } - const CBlockIndex *forked; - BOOST_FOREACH(const CBlockIndex* block, setTips) - { - // We skip anything over notarised height to avoid breaking normal consensus rules. - if ( block->GetHeight() > notarized_height || block->nTime > notarized_timestamp ) - continue; - // We can also check if the block is in the active chain as a backup test. - forked = chainActive.FindFork(block); - // Here we save each forked block to a vector for removal later. - if ( forked != 0 ) - prunedblocks.push_back(block); - } - if (prunedblocks.size() > 0 && pblocktree->EraseBatchSync(prunedblocks)) - { - // Blocks cleared from disk succesfully, using internal DB batch erase function. Which exists, but has never been used before. - // We need to try and clear the block index from mapBlockIndex now, otherwise node will need a restart. - BOOST_FOREACH(const CBlockIndex* block, prunedblocks) - { - m++; - mapBlockIndex.erase(block->GetBlockHash()); - } - LogPrintf( "%s removed %d orphans from %d blocks before %d\n",ASSETCHAINS_SYMBOL,m,n, notarized_height); - return true; - } - return false; -} - /*uint64_t myGettxout(uint256 hash,int32_t n) { CCoins coins; @@ -2205,7 +2199,12 @@ bool myAddtomempool(CTransaction &tx, CValidationState *pstate, bool fSkipExpiry pstate = &state; CTransaction Ltx; bool fMissingInputs,fOverrideFees = false; if ( mempool.lookup(tx.GetHash(),Ltx) == 0 ) - return(AcceptToMemoryPool(mempool, *pstate, tx, false, &fMissingInputs, !fOverrideFees, -1, fSkipExpiry)); + { + if ( !fSkipExpiry ) + return(AcceptToMemoryPool(mempool, *pstate, tx, false, &fMissingInputs, !fOverrideFees, -1)); + else + return(CCTxFixAcceptToMemPoolUnchecked(mempool,tx)); + } else return(true); } @@ -3389,9 +3388,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); int32_t futureblock; - CAmount blockReward = 0; uint64_t notarypaycheque = 0; + CAmount blockReward = GetBlockSubsidy(pindex->GetHeight(), chainparams.GetConsensus()); + uint64_t notarypaycheque = 0; // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in - if (!CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck) || futureblock != 0 ) + if ( !CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck) || futureblock != 0 ) { //LogPrintf("checkblock failure in connectblock futureblock.%d\n",futureblock); return false; @@ -3513,7 +3513,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CAmount nFees = 0; int nInputs = 0; uint64_t valueout; - int64_t voutsum = 0,prevsum=0,interest,sum = 0; + int64_t voutsum = 0, prevsum = 0, interest, sum = 0, stakeTxValue = 0; unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; @@ -3633,8 +3633,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): voutsum less after adding valueout"),REJECT_INVALID,"tx valueout is too big");*/ if (!tx.IsCoinBase()) { - nFees += view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime) - valueout; + nFees += (stakeTxValue= view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime) - valueout); sum += interest; + //LogPrintf( "tx.%s nFees.%li interest.%li\n", tx.GetHash().ToString().c_str(), stakeTxValue, interest); std::vector vChecks; if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) @@ -3690,6 +3691,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } + + // This is moved from CheckBlock for staking chains, so we can enforce the staking tx value was indeed paid to the coinbase. + //LogPrintf( "blockReward.%li stakeTxValue.%li sum.%li\n",blockReward,stakeTxValue,sum); + if ( ASSETCHAINS_STAKED != 0 && fCheckPOW && komodo_checkPOW(blockReward+stakeTxValue-notarypaycheque,1,(CBlock *)&block,pindex->GetHeight()) < 0 ) + return state.DoS(100, error("ConnectBlock: ac_staked chain failed slow komodo_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW"); view.PushAnchor(sprout_tree); view.PushAnchor(sapling_tree); @@ -3710,7 +3716,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); - blockReward += nFees + GetBlockSubsidy(pindex->GetHeight(), chainparams.GetConsensus()) + sum; + blockReward += nFees + sum; if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && { uint64_t checktoshis; @@ -4077,9 +4083,9 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { CTransaction &tx = block.vtx[i]; list removed; CValidationState stateDummy; - + // don't keep staking or invalid transactions - if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),true) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + if (tx.IsCoinBase() || (i == block.vtx.size()-1 && komodo_newStakerActive(0, pindexDelete->nTime) == 0 && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),0) != 0) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { mempool.remove(tx, removed, true); } @@ -4111,17 +4117,14 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { { CTransaction &tx = block.vtx[i]; //if ((i == (block.vtx.size() - 1)) && ((ASSETCHAINS_LWMAPOS && block.IsVerusPOSBlock()) || (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block) != 0)))) - if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),true) != 0))) + if ( komodo_newStakerActive(0, pindexDelete->nTime) == 0 && i == block.vtx.size()-1 && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),0) != 0 ) { #ifdef ENABLE_WALLET - if ( !GetBoolArg("-disablewallet", false) ) - pwalletMain->EraseFromWallet(tx.GetHash()); + // new staking tx cannot be accepted to mempool and expires in 1 block, so no need for this! :D + if ( !GetBoolArg("-disablewallet", false) ) + pwalletMain->EraseFromWallet(tx.GetHash()); #endif - } - else - { - SyncWithWallets(tx, NULL); - } + } else SyncWithWallets(tx, NULL); } // Update cached incremental witnesses GetMainSignals().ChainTip(pindexDelete, &block, newSproutTree, newSaplingTree, false); @@ -5084,7 +5087,7 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, } int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtime); -int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height); +int32_t komodo_checkPOW(int64_t stakeTxValue,int32_t slowflag,CBlock *pblock,int32_t height); bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, @@ -5116,7 +5119,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C LogPrintf(" failed hash ht.%d\n",height); return state.DoS(50, error("CheckBlock: proof of work failed"),REJECT_INVALID, "high-hash"); } - if ( komodo_checkPOW(1,(CBlock *)&block,height) < 0 ) // checks Equihash + if ( ASSETCHAINS_STAKED == 0 && komodo_checkPOW(0,1,(CBlock *)&block,height) < 0 ) // checks Equihash return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW"); } // Check the merkle root. @@ -5188,10 +5191,9 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C list removed; for (i=0; iGetHeight()); checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); bool fRequested = MarkBlockAsReceived(hash); - // Test thing on LABS to test viability of rejecting a node pushing a chain. - // Supposed to stop malicious forks being pushed. Untested. Disabled incase of problems it may cause. - if ( 0 && pfrom && !fRequested && vNodes.size() > 1 ) - { - pfrom->nBlocksinARow += 1; - if ( pfrom->nBlocksinARow >= 10 ) - { - pfrom->nBlocksinARow2 += 1; - if ( pfrom->nBlocksinARow2 > 5 ) - { - pfrom->nBlocksinARow = 0; - pfrom->nBlocksinARow2 = 0; - LogPrintf( "reset node.%i\n",(int32_t)pfrom->GetId()); - } - else - { - LogPrintf( "Requesting new peer node.%i blocksinrow.%i blocsinrow2.%i\n",(int32_t)pfrom->GetId(),pfrom->nBlocksinARow,pfrom->nBlocksinARow2); - return(false); - } - } - } fRequested |= fForceProcessing; - if ( checked != 0 && komodo_checkPOW(0,pblock,height) < 0 ) //from_miner && ASSETCHAINS_STAKED == 0 + if ( checked != 0 && komodo_checkPOW(0,0,pblock,height) < 0 ) //from_miner && ASSETCHAINS_STAKED == 0 { checked = 0; //LogPrintf("passed checkblock but failed checkPOW.%d\n",from_miner && ASSETCHAINS_STAKED == 0); @@ -7127,8 +7111,10 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { + int32_t nProtocolVersion; const CChainParams& chainparams = Params(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); + //if ( KOMODO_NSPV_SUPERLITE ) //LogPrintf( "recv: %s peer=%d\n", SanitizeString(strCommand).c_str(), (int32_t)pfrom->GetId()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { @@ -7181,7 +7167,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - + if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { diff --git a/src/main.h b/src/main.h index c139a785..ffb5faa4 100644 --- a/src/main.h +++ b/src/main.h @@ -291,7 +291,7 @@ void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee=false, int dosLevel=-1, bool fSkipExpiry=false); + bool* pfMissingInputs, bool fRejectAbsurdFee=false, int dosLevel=-1); struct CNodeStateStats { @@ -716,7 +716,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); /** Transaction validation functions */ /** Context-independent validity checks */ -bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState& state, libzcash::ProofVerifier& verifier); +bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState& state, libzcash::ProofVerifier& verifier, int32_t txIndex, int32_t numTxs); bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransaction& tx, CValidationState &state); /** Check for standard transaction types @@ -808,7 +808,6 @@ bool GetAddressUnspent(uint160 addressHash, int type, bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos,bool checkPOW); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW); -bool RemoveOrphanedBlocks(int32_t notarized_height); bool PruneOneBlockFile(bool tempfile, const int fileNumber); /** Functions for validating blocks and updating the block tree */ diff --git a/src/miner.cpp b/src/miner.cpp index 62e3b822..e4a3db79 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -146,9 +146,11 @@ int32_t komodo_longestchain(); int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); int64_t komodo_block_unlocktime(uint32_t nHeight); uint64_t komodo_commission(const CBlock *block,int32_t height); -int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig); +int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig, uint256 merkleroot); int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); -int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); +uint256 komodo_calcmerkleroot(CBlock *pblock, uint256 prevBlockHash, int32_t nHeight, bool fNew, CScript scriptPubKey); +int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); +int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void* ptr); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); int32_t komodo_is_notarytx(const CTransaction& tx); CScript Marmara_scriptPubKey(int32_t height,CPubKey pk); @@ -158,6 +160,30 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestam int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); CScript komodo_mineropret(int32_t nHeight); bool komodo_appendACscriptpub(); +CScript komodo_makeopret(CBlock *pblock, bool fNew); + +int32_t komodo_waituntilelegible(uint32_t blocktime, int32_t stakeHeight, uint32_t delay) +{ + int64_t adjustedtime = (int64_t)GetAdjustedTime(); + while ( (int64_t)blocktime-ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX > adjustedtime ) + { + int64_t secToElegible = (int64_t)blocktime-ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX-adjustedtime; + if ( delay <= ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF && secToElegible <= ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF ) + break; + if ( (rand() % 100) < 2-(secToElegible>ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX) ) + LogPrintf( "[%s:%i] %llds until elegible...\n", ASSETCHAINS_SYMBOL, stakeHeight, (long long)secToElegible); + if ( chainActive.LastTip()->GetHeight() >= stakeHeight ) + { + LogPrintf( "[%s:%i] Chain advanced, reset staking loop.\n", ASSETCHAINS_SYMBOL, stakeHeight); + return(0); + } + if( !GetBoolArg("-gen",false) ) + return(0); + sleep(1); + adjustedtime = (int64_t)GetAdjustedTime(); + } + return(1); +} CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -277,6 +303,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { + //break; // dont add any tx to block.. debug for KMD fix. Disabled. const CTransaction& tx = mi->GetTx(); int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) @@ -296,7 +323,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //voutsum += txvalue; if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { - //LogPrintf("CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); + LogPrintf("CreateNewBlock: komodo_validate_interest failure txid.%s nHeight.%d nTime.%u vs locktime.%u\n",tx.GetHash().ToString().c_str(),nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); continue; } @@ -357,7 +384,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 uint8_t *script; int32_t scriptlen; uint256 hash; CTransaction tx1; // loop over notaries array and extract index of signers. - if ( fToCryptoAddress && GetTransaction(txin.prevout.hash,tx1,hash,false) ) + if ( fToCryptoAddress && myGetTransaction(txin.prevout.hash,tx1,hash) ) { for (int8_t i = 0; i < numSN; i++) { @@ -559,6 +586,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 blocktime = 1 + std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); //pblock->nTime = blocktime + 1; pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + //LogPrintf( "nBits.%u\n",pblock->nBits); int32_t stakeHeight = chainActive.Height() + 1; @@ -584,25 +612,33 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } else { - blocktime = GetAdjustedTime(); - //if ( blocktime > pindexPrev->GetMedianTimePast()+60 ) - // blocktime = pindexPrev->GetMedianTimePast() + 60; - siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig); - // if you skip this check it will create a block too far into the future and not pass ProcessBlock or AcceptBlock. - // This has been moved from the mining loop to save CPU, and to also make ac_staked work with the verus miner. - while ( blocktime-57 > GetAdjustedTime() ) + blocktime = GetAdjustedTime(); + uint256 merkleroot = komodo_calcmerkleroot(pblock, pindexPrev->GetBlockHash(), nHeight, true, scriptPubKeyIn); + //LogPrintf( "MINER: merkleroot.%s\n", merkleroot.GetHex().c_str()); + /* + Instead of a split RPC and writing heaps of code, I added this. + It works with the consensus, because the stakeTx valuein-valueout+blockReward is enforced for the coinbase of staking blocks. + For example: + utxovalue = 30; + 30% of the value of the staking utxo is added to the coinbase the same as fees,returning the remaining 70% to the address that staked. + utxovalue can be adjusted from any number 0 to 100 via the setstakingsplit RPC. + Can also be set with -splitperc= command line arg, or conf file. + 0 means that it functions as it did previously (default). + 100 means it automates the pos64staker in the daemon, combining the stake tx and the coinbase to an address. Either to -pubkey or a new address from the keystore. + Mining with a % here and without pubkey will over time create varied sized utxos, and evenly distribute them over many addresses and segids. + */ { - sleep(1); - if ( (rand() % 100) < 1 ) - LogPrintf( "%u seconds until elegible, waiting.\n", blocktime-((uint32_t)GetAdjustedTime()+57)); - if ( chainActive.LastTip()->GetHeight() >= stakeHeight ) - { - LogPrintf( "Block Arrived, reset staking loop.\n"); - return(0); - } - if( !GetBoolArg("-gen",false) ) - return(0); + LOCK(cs_main); + utxovalue = ASSETCHAINS_STAKED_SPLIT_PERCENTAGE; } + + siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig, merkleroot); + if ( komodo_newStakerActive(nHeight, blocktime) != 0 ) + nFees += utxovalue; + //LogPrintf( "added to coinbase.%llu staking tx valueout.%llu\n", (long long unsigned)utxovalue, (long long unsigned)txStaked.vout[0].nValue); + uint32_t delay = ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH ? ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX : ASSETCHAINS_STAKED_BLOCK_FUTURE_HALF; + if ( komodo_waituntilelegible(blocktime, stakeHeight, delay) == 0 ) + return(0); } if ( siglen > 0 ) @@ -624,13 +660,15 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight); txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; - + txNew.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(1)) + COINBASE_FLAGS; txNew.vout.resize(1); txNew.vout[0].scriptPubKey = scriptPubKeyIn; txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees; - //LogPrintf("mine ht.%d with %.8f\n",nHeight,(double)txNew.vout[0].nValue/COIN); txNew.nExpiryHeight = 0; + //LogPrintf( "coinbase txid.%s\n", txNew.GetHash().ToString().c_str()); + //LogPrintf( "MINER: coinbasetx.%s\n", EncodeHexTx(txNew).c_str()); + //LogPrintf("mine ht.%d with %.8f\n",nHeight,(double)txNew.vout[0].nValue/COIN); + txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 ) @@ -768,7 +806,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 ) { - uint32_t r; + uint32_t r; CScript opret; void **ptr=0; CMutableTransaction txNotary = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1); if ( pblock->nTime < pindexPrev->nTime+60 ) pblock->nTime = pindexPrev->nTime + 60; @@ -782,7 +820,15 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 memcpy(&r,&randvals,sizeof(r)); pblock->nTime += (r % (33 - gpucount)*(33 - gpucount)); } - if ( komodo_notaryvin(txNotary,NOTARY_PUBKEY33) > 0 ) + pblock->vtx[0] = txNew; + if ( Mining_height > nDecemberHardforkHeight ) //December 2019 hardfork + { + opret = komodo_makeopret(pblock, true); + ptr = (void**)calloc(0,sizeof(void *)*2); + ptr[0] = (void*)(CScript*)&opret; + ptr[1] = (void*)(unsigned long long)pblock->nTime; + } + if ( komodo_notaryvin(txNotary,NOTARY_PUBKEY33,ptr) > 0 ) { CAmount txfees = 5000; pblock->vtx.push_back(txNotary); @@ -791,7 +837,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 nFees += txfees; pblocktemplate->vTxFees[0] = -nFees; //*(uint64_t *)(&pblock->vtx[0].vout[0].nValue) += txfees; - //LogPrintf("added notaryvin\n"); + LogPrintf("added notaryvin includes proof.%i\n", ptr!=0); } else { @@ -803,6 +849,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } return(0); } + if ( ptr!=0 ) free(ptr); } else if ( ASSETCHAINS_CC == 0 && pindexPrev != 0 && ASSETCHAINS_STAKED == 0 && (ASSETCHAINS_SYMBOL[0] != 0 || IS_KOMODO_NOTARY == 0 || My_notaryid < 0) ) { @@ -1049,7 +1096,7 @@ static bool ProcessBlockFound(CBlock* pblock) int32_t komodo_baseid(char *origbase); int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t *blocktimes,int32_t *nonzpkeysp,int32_t height); -arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc); +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive); int32_t FOUND_BLOCK,KOMODO_MAYBEMINED; extern int32_t KOMODO_LASTMINED,KOMODO_INSYNC; int32_t roundrobin_delay; @@ -1459,7 +1506,7 @@ void static BitcoinMiner_noeq() if ( ASSETCHAINS_STAKED != 0 ) { int32_t percPoS,z; bool fNegative,fOverflow; - HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); + HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED,komodo_newStakerActive(Mining_height, pblock->nTime)); HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); LogPrintf("Block %d : PoS %d%% vs target %d%%\n", Mining_height, percPoS, (int32_t)ASSETCHAINS_STAKED); } @@ -1619,6 +1666,7 @@ void static BitcoinMiner_noeq() } int32_t gotinvalid; +extern int32_t getkmdseason(int32_t height); #ifdef ENABLE_WALLET void static BitcoinMiner(CWallet *pwallet) @@ -1654,7 +1702,7 @@ void static BitcoinMiner() break; } if ( ASSETCHAINS_SYMBOL[0] == 0 ) - komodo_chosennotary(¬aryid,chainActive.LastTip()->GetHeight(),NOTARY_PUBKEY33,(uint32_t)chainActive.LastTip()->GetBlockTime()); + komodo_chosennotary(¬aryid,chainActive.Height()+1,NOTARY_PUBKEY33,(uint32_t)chainActive.Tip()->GetMedianTimePast()); if ( notaryid != My_notaryid ) My_notaryid = notaryid; std::string solver; @@ -1664,7 +1712,7 @@ void static BitcoinMiner() solver = "default"; assert(solver == "tromp" || solver == "default"); LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); - if ( ASSETCHAINS_SYMBOL[0] != 0 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 ) LogPrintf("notaryid.%d Mining.%s with %s\n",notaryid,ASSETCHAINS_SYMBOL,solver.c_str()); std::mutex m_cs; bool cancelSolver = false; @@ -1728,7 +1776,7 @@ void static BitcoinMiner() #endif if ( ptr == 0 ) { - if ( !GetBoolArg("-gen",false)) + if ( 0 && !GetBoolArg("-gen",false)) { miningTimer.stop(); c.disconnect(); @@ -1778,6 +1826,9 @@ void static BitcoinMiner() } else LogPrintf("%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT); } } + // We cant increment nonce for proof transactions, as it modifes the coinbase, meaning CreateBlock must be called again to get a new valid proof to pass validation. + if ( (ASSETCHAINS_SYMBOL[0] == 0 && notaryid >= 0 && Mining_height > nDecemberHardforkHeight ) || (ASSETCHAINS_STAKED != 0 && komodo_newStakerActive(Mining_height, pblock->nTime) != 0) ) //December 2019 hardfork + nExtraNonce = 0; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); //LogPrintf("Running KomodoMiner.%s with %u transactions in block\n",solver.c_str(),(int32_t)pblock->vtx.size()); LogPrintf("Running KomodoMiner.%s with %u transactions in block (%u bytes)\n",solver.c_str(),pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION)); @@ -1794,10 +1845,10 @@ void static BitcoinMiner() j = 65; if ( (Mining_height >= 235300 && Mining_height < 236000) || (Mining_height % KOMODO_ELECTION_GAP) > 64 || (Mining_height % KOMODO_ELECTION_GAP) == 0 || Mining_height > 1000000 ) { - int32_t dispflag = 0; - if ( notaryid <= 3 || notaryid == 32 || (notaryid >= 43 && notaryid <= 45) &¬aryid == 51 || notaryid == 52 || notaryid == 56 || notaryid == 57 ) + int32_t dispflag = 1; // TODO: set this back to 0 when finished testing. + if ( notaryid <= 3 || notaryid == 32 || (notaryid >= 43 && notaryid <= 45) || notaryid == 51 || notaryid == 52 || notaryid == 56 || notaryid == 57 ) dispflag = 1; - komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,pindexPrev->GetHeight()); + komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,Mining_height); if ( nonzpkeys > 0 ) { for (i=0; i<33; i++) @@ -1832,19 +1883,20 @@ void static BitcoinMiner() break; if ( j == 65 ) KOMODO_LASTMINED = 0; - } else LogPrintf("no nonz pubkeys\n"); + } else LogPrintf("ht.%i all NN are elegible\n",Mining_height); //else LogPrintf("no nonz pubkeys\n"); + if ( (Mining_height >= 235300 && Mining_height < 236000) || (j == 65 && Mining_height > KOMODO_MAYBEMINED+1 && Mining_height > KOMODO_LASTMINED+64) ) { HASHTarget = arith_uint256().SetCompact(KOMODO_MINDIFF_NBITS); LogPrintf("I am the chosen one for %s ht.%d\n",ASSETCHAINS_SYMBOL,pindexPrev->GetHeight()+1); - } //else LogPrintf("duplicate at j.%d\n",j); + } else LogPrintf("duplicate at j.%d\n",j); } else Mining_start = 0; } else Mining_start = 0; if ( ASSETCHAINS_STAKED > 0 ) { int32_t percPoS,z; bool fNegative,fOverflow; - HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); + HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED,komodo_newStakerActive(Mining_height, pblock->nTime)); HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); if ( ASSETCHAINS_STAKED < 100 ) LogPrintf("Block %d : PoS %d%% vs target %d%% \n",Mining_height,percPoS,(int32_t)ASSETCHAINS_STAKED); @@ -1873,15 +1925,15 @@ void static BitcoinMiner() crypto_generichash_blake2b_update(&curr_state,pblock->nNonce.begin(),pblock->nNonce.size()); // (x_1, x_2, ...) = A(I, V, n, k) LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",solver, pblock->nNonce.ToString()); - arith_uint256 hashTarget; + arith_uint256 hashTarget,hashTarget_POW = HASHTarget_POW; if ( KOMODO_MININGTHREADS > 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 && Mining_height > 10 ) hashTarget = HASHTarget_POW; else hashTarget = HASHTarget; std::function)> validBlock = #ifdef ENABLE_WALLET - [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams] + [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams, &hashTarget_POW] #else - [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams] + [&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams, &hashTarget_POW] #endif (std::vector soln) { int32_t z; arith_uint256 h; CBlock B; @@ -1935,10 +1987,22 @@ void static BitcoinMiner() } else { + if ( KOMODO_MININGTHREADS == 0 ) // we are staking + { + // Need to rebuild block if the found solution for PoS, meets POW target, otherwise it will be rejected. + if ( ASSETCHAINS_STAKED < 100 && komodo_newStakerActive(Mining_height,pblock->nTime) != 0 && h < hashTarget_POW ) + { + LogPrintf( "[%s:%d] PoS block.%u meets POW_Target.%u building new block\n", ASSETCHAINS_SYMBOL, Mining_height, h.GetCompact(), hashTarget_POW.GetCompact()); + return(false); + } + if ( komodo_waituntilelegible(B.nTime, Mining_height, ASSETCHAINS_STAKED_BLOCK_FUTURE_MAX) == 0 ) + return(false); + } uint256 tmp = B.GetHash(); + LogPrintf("[%s:%d] mined block ",ASSETCHAINS_SYMBOL,Mining_height); int32_t z; for (z=31; z>=0; z--) LogPrintf("%02x",((uint8_t *)&tmp)[z]); - LogPrintf(" mined %s block %d!\n",ASSETCHAINS_SYMBOL,Mining_height); + LogPrintf( "\n"); } CValidationState state; if ( !TestBlockValidity(state,B, chainActive.LastTip(), true, false)) diff --git a/src/net.h b/src/net.h index cef261b3..71d993b0 100644 --- a/src/net.h +++ b/src/net.h @@ -323,9 +323,6 @@ class CNode bool fNetworkNode; bool fSuccessfullyConnected; bool fDisconnect; - // count blocks seen. - int8_t nBlocksinARow; - int8_t nBlocksinARow2; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs diff --git a/src/notaries_staked.cpp b/src/notaries_staked.cpp index 551d5a43..8d0ffbec 100644 --- a/src/notaries_staked.cpp +++ b/src/notaries_staked.cpp @@ -2,13 +2,10 @@ #include "notaries_staked.h" #include "crosschain.h" #include "cc/CCinclude.h" +#include "komodo_defs.h" #include -extern char NOTARYADDRS[64][64]; -extern std::string NOTARY_ADDRESS,NOTARY_PUBKEY; -extern int32_t STAKED_ERA,IS_STAKED_NOTARY,IS_KOMODO_NOTARY; extern pthread_mutex_t staked_mutex; -extern uint8_t NOTARY_PUBKEY33[33]; int8_t is_STAKED(const char *chain_name) { diff --git a/src/pow.cpp b/src/pow.cpp index e9abece9..b064c91a 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -380,6 +380,8 @@ int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33, int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],uint32_t blocktimes[66],int32_t height,uint8_t pubkey33[33],uint32_t blocktime); int32_t komodo_currentheight(); void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); +bool komodo_checkopret(CBlock *pblock, CScript &merkleroot); +CScript komodo_makeopret(CBlock *pblock, bool fNew); extern int32_t KOMODO_CHOSEN_ONE; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; #define KOMODO_ELECTION_GAP 2000 @@ -440,8 +442,14 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t } if ( (flag != 0 || special2 > 0) && special2 != -2 ) { - //LogPrintf("EASY MINING ht.%d\n",height); bnTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); + const void* pblock = &blkHeader; + CScript merkleroot = CScript(); + if ( height > nDecemberHardforkHeight && !komodo_checkopret((CBlock*)pblock, merkleroot) ) // December 2019 hardfork + { + LogPrintf( "failed or missing expected.%s != %s\n", komodo_makeopret((CBlock*)pblock, false).ToString().c_str(), merkleroot.ToString().c_str()); + return false; + } } } } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 8b10bad1..af5cd398 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -53,7 +53,8 @@ using namespace std; extern int32_t ASSETCHAINS_FOUNDERS; uint64_t komodo_commission(const CBlock *pblock,int32_t height); int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex); -arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc); +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc,int32_t newStakerActive); +int32_t komodo_newStakerActive(int32_t height, uint32_t timestamp); /** * Return average network hashes per second based on the last 'lookup' blocks, @@ -831,12 +832,16 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if ( ASSETCHAINS_STAKED != 0 ) { arith_uint256 POWtarget; int32_t PoSperc; - POWtarget = komodo_PoWtarget(&PoSperc,hashTarget,(int32_t)(pindexPrev->GetHeight()+1),ASSETCHAINS_STAKED); + POWtarget = komodo_PoWtarget(&PoSperc,hashTarget,(int32_t)(pindexPrev->GetHeight()+1),ASSETCHAINS_STAKED,komodo_newStakerActive(chainActive.Height()+1, pblock->nTime)); result.push_back(Pair("target", POWtarget.GetHex())); result.push_back(Pair("PoSperc", (int64_t)PoSperc)); result.push_back(Pair("ac_staked", (int64_t)ASSETCHAINS_STAKED)); result.push_back(Pair("origtarget", hashTarget.GetHex())); - } else result.push_back(Pair("target", hashTarget.GetHex())); + } + /*else if ( ASSETCHAINS_ADAPTIVEPOW > 0 ) + result.push_back(Pair("target",komodo_adaptivepow_target((int32_t)(pindexPrev->GetHeight()+1),hashTarget,pblock->nTime).GetHex()));*/ + else + result.push_back(Pair("target", hashTarget.GetHex())); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); diff --git a/src/version.h b/src/version.h index cd73adae..f2fbbac0 100644 --- a/src/version.h +++ b/src/version.h @@ -49,4 +49,4 @@ static const int MEMPOOL_GD_VERSION = 60002; //! "filter*" commands are disabled without NODE_BLOOM after and including this version static const int NO_BLOOM_VERSION = 170004; -#endif // BITCOIN_VERSION_H +#endif // BITCOIN_VERSION_H \ No newline at end of file diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index eb3b0b7b..535fc689 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5261,7 +5261,7 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp) int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); extern std::string NOTARY_PUBKEY; -int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) +int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void *pTr) { set setAddress; uint8_t *script,utxosig[128]; uint256 utxotxid; uint64_t utxovalue; int32_t i,siglen=0,nMinDepth = 1,nMaxDepth = 9999999; vector vecOutputs; uint32_t utxovout,eligible,earliest = 0; CScript best_scriptPubKey; bool fNegative,fOverflow; bool signSuccess; SignatureData sigdata; uint64_t txfee; uint8_t *ptr; @@ -5314,7 +5314,7 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) //LogPrintf("check %s/v%d %llu\n",(char *)utxotxid.GetHex().c_str(),utxovout,(long long)utxovalue); txNew.vin.resize(1); - txNew.vout.resize(1); + txNew.vout.resize((pTr!=0)+1); txfee = utxovalue / 2; //for (i=0; i<32; i++) // ((uint8_t *)&revtxid)[i] = ((uint8_t *)&utxotxid)[31 - i]; @@ -5322,6 +5322,13 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) txNew.vin[0].prevout.n = utxovout; txNew.vout[0].nValue = utxovalue - txfee; txNew.vout[0].scriptPubKey = CScript() << ParseHex(CRYPTO777_PUBSECPSTR) << OP_CHECKSIG; + if ( pTr != 0 ) + { + void **p = (void**)pTr; + txNew.vout[1].nValue = 0; + txNew.vout[1].scriptPubKey = *(CScript*)p[0]; + txNew.nLockTime = (uint32_t)(unsigned long long)p[1]; + } CTransaction txNewConst(txNew); signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, utxovalue, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); if (!signSuccess) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 7c2d3fb2..5ec420c6 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -489,7 +489,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, // ac_public chains set at height like KMD and ZEX, will force a rescan if we dont ignore this error: bad-txns-acpublic-chain // there cannot be any ztx in the wallet on ac_public chains that started from block 1, so this wont affect those. // PIRATE fails this check for notary nodes, need exception. Triggers full rescan without it. - if ( !(CheckTransaction(0,wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid()) && (state.GetRejectReason() != "bad-txns-acpublic-chain" && state.GetRejectReason() != "bad-txns-acprivacy-chain") ) + if ( !(CheckTransaction(0,wtx, state, verifier, 0, 0) && (wtx.GetHash() == hash) && state.IsValid()) && (state.GetRejectReason() != "bad-txns-acpublic-chain" && state.GetRejectReason() != "bad-txns-acprivacy-chain" && state.GetRejectReason() != "bad-txns-stakingtx") ) { //fprintf(stderr, "tx failed: %s rejectreason.%s\n", wtx.GetHash().GetHex().c_str(), state.GetRejectReason().c_str()); // vin-empty on staking chains is error relating to a failed staking tx, that for some unknown reason did not fully erase. save them here to erase and re-add later on. From 356dff91dfeba3fc91db888d88a0cd914b371579 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 16:44:54 +0300 Subject: [PATCH 02/12] v0.5.0 port (part 2) port of https://github.com/KomodoPlatform/komodo/commit/3fa95f0fd5ef43d2a6f7981c476ef04ee56d9164 to komodo-qt. NB! This commit skip all CC changes, for CC enabled chains which are really using CC we will add needed changes later. For now komodo-qt is supposed to work with KMD itself and also PoW and PoS chains, which is not actively using CC. --- src/rpc/client.cpp | 3 +- src/rpc/server.cpp | 3 +- src/rpc/server.h | 3 +- src/wallet/asyncrpcoperation_sendmany.cpp | 23 +++++++++++ src/wallet/rpcwallet.cpp | 48 +++++++++++++++++++++-- src/wallet/wallet.cpp | 10 +++-- src/wallet/walletdb.cpp | 2 +- 7 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 9e1accd7..4218c2e2 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -120,6 +120,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "prioritisetransaction", 2 }, { "setban", 2 }, { "setban", 3 }, + { "setstakingsplit", 0 }, { "getblockhashes", 0 }, { "getblockhashes", 1 }, { "getblockhashes", 2 }, diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 6076ee14..20229225 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -5,7 +5,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -622,6 +622,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "sendtoaddress", &sendtoaddress, false }, { "wallet", "setaccount", &setaccount, true }, { "wallet", "setpubkey", &setpubkey, true }, + { "wallet", "setstakingsplit", &setstakingsplit, true }, { "wallet", "settxfee", &settxfee, true }, { "wallet", "signmessage", &signmessage, true }, { "wallet", "walletlock", &walletlock, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 606d7453..9ee71e35 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -399,6 +399,7 @@ extern UniValue getiguanajson(const UniValue& params, bool fHelp); extern UniValue getnotarysendmany(const UniValue& params, bool fHelp); extern UniValue geterablockheights(const UniValue& params, bool fHelp); extern UniValue setpubkey(const UniValue& params, bool fHelp); +extern UniValue setstakingsplit(const UniValue& params, bool fHelp); extern UniValue getwalletinfo(const UniValue& params, bool fHelp); extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 8fd5e901..98344162 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -57,6 +57,7 @@ extern char ASSETCHAINS_SYMBOL[65]; int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int32_t komodo_blockheight(uint256 hash); int tx_height( const uint256 &hash ); +bool komodo_hardfork_active(uint32_t time); extern UniValue signrawtransaction(const UniValue& params, bool fHelp); extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); @@ -375,7 +376,11 @@ bool AsyncRPCOperation_sendmany::main_impl() { // locktime to spend time locked coinbases if (ASSETCHAINS_SYMBOL[0] == 0) { + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) builder_.SetLockTime((uint32_t)time(NULL) - 60); // set lock time for Komodo interest + else + builder_.SetLockTime((uint32_t)chainActive.Tip()->GetMedianTimePast()); } } else { CMutableTransaction rawTx(tx_); @@ -388,7 +393,11 @@ bool AsyncRPCOperation_sendmany::main_impl() { } if (ASSETCHAINS_SYMBOL[0] == 0) { + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) rawTx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 + else + rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); } tx_ = CTransaction(rawTx); } @@ -590,7 +599,12 @@ bool AsyncRPCOperation_sendmany::main_impl() { CMutableTransaction mtx(tx_); crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); mtx.joinSplitPubKey = joinSplitPubKey_; + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) mtx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 + else + mtx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); + tx_ = CTransaction(mtx); // Copy zinputs and zoutputs to more flexible containers @@ -1361,7 +1375,12 @@ void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() { CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) rawTx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 + else + rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); + tx_ = CTransaction(rawTx); } @@ -1387,7 +1406,11 @@ void AsyncRPCOperation_sendmany::add_taddr_change_output_to_tx(CBitcoinAddress * CMutableTransaction rawTx(tx_); rawTx.vout.push_back(out); + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) rawTx.nLockTime = (uint32_t)time(NULL) - 60; // jl777 + else + rawTx.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); tx_ = CTransaction(rawTx); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 535fc689..6258c86e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5467,9 +5467,6 @@ UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vector 1 ) + throw runtime_error( + "setstakingsplit\n" + "\nSets the split ratio as a percentage for the PoS64 staker. Sends entered % of staking tx value to the mined coinbase.\n" + "\nArguments:\n" + "1. \"split_percentage\" (numeric) split ratio range 0-100.\n" + "\nResult:\n" + " {\n" + " \"split_percentage\" : \"split_percentage\" (numeric) range 0-100\n" + " }\n" + "\nExamples:\n" + + HelpExampleCli("setstakingsplit", "0") + + HelpExampleRpc("setstakingsplit", "100") + ); + + LOCK(cs_main); + if ( komodo_newStakerActive(chainActive.Height(),(uint32_t)time(NULL)) != 1 ) + { + throw runtime_error("New PoS64 staker not active yet\n"); + } + if ( params.size() == 0 ) + { + result.push_back(Pair("split_percentage", ASSETCHAINS_STAKED_SPLIT_PERCENTAGE)); + } + else + { + std::string strperc = params[0].get_str(); + int32_t perc = std::stoi(strperc); + if ( perc >= 0 && perc <= 100 ) + { + + ASSETCHAINS_STAKED_SPLIT_PERCENTAGE = perc; + result.push_back(Pair("split_percentage", perc)); + } + else + { + throw runtime_error("must be between 0 and 100 inclusive.\n"); + } + } + return result; +} + UniValue channelsaddress(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector pubkey; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9c413aea..0be7d7ec 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1805,7 +1805,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl for (size_t i = 0; i < tx.vin.size(); i++) { uint256 hash; CTransaction txin; CTxDestination address; - if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) + if ( myGetTransaction(tx.vin[i].prevout.hash,txin,hash) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) { if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; @@ -3798,9 +3798,13 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); int nextBlockHeight = chainActive.Height() + 1; - CMutableTransaction txNew = CreateNewContextualCMutableTransaction( - Params().GetConsensus(), nextBlockHeight); + CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); + + //if ((uint32_t)chainActive.LastTip()->nTime < ASSETCHAINS_STAKED_HF_TIMESTAMP) + if ( !komodo_hardfork_active((uint32_t)chainActive.LastTip()->nTime) ) txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now + else + txNew.nLockTime = (uint32_t)chainActive.Tip()->GetMedianTimePast(); // Activates after Overwinter network upgrade if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 5ec420c6..03c6c150 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * From 9b67de8476688b17aa8c2f8472d450170a7c1c67 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 16:47:38 +0300 Subject: [PATCH 03/12] use kmd metrics art to show it start daemon with `-showmetrics=1` --- src/metrics.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/metrics.h b/src/metrics.h index 84ea9ed5..6c612631 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -100,23 +100,23 @@ void ThreadShowMetricsScreen(); * Heart: img2txt -W 40 -H 20 -f utf8 -d none 2000px-Heart_corazón.svg.png */ const std::string METRICS_ART = -"   \n" -"   \n" -"  :88SX@888@@X8:  8; %X X% ;8 \n" -"  %%Xt%tt%SSSSS:XXXt@@  X :: :: X \n" -"  @S;;tt%%%t ;;::XXXXSX  % SS % \n" -"  .t:::;;%8888 88888tXXXX8;  S S \n" -"  .%...:::8 8::XXX%;  X X \n" -"  8888...:t888888X 8t;;::XX8   8 8 \n" -" %888888...:::;:8  :Xttt;;;::X@    \n" -" 888888888...:St 8:%%tttt;;;:X  X X \n" -" 88888888888S8  :%;ttt%%tttt;;X  8 8 \n" -" %888888888%t 8S:;;;tt%%%ttt;8  : : \n" -"  8t8888888  S8888888Stt%%%t@   :: :: \n" -"  .@tt888@ 8;;ttt@;  t t \n" -"  .8ttt8@SSSSS SXXXX%:;;;X;  8 8 \n" -"  X8ttt8888% %88...::X8   X. .X \n" -"  %8@tt88;8888%8888%8X   :; ;: \n" -"  :@888@XXX@888:  tt \n" -"   \n" -"   "; +" 8SSS%%%%tt8@ \n" +" X@SX@@88@XS%%%%%%%%t8 \n" +" @ %SS88888888@8%%%%%%%t@ :@. .Xt ;@. .Xt \n" +" S :88: .S%%%%%%%%% 8 X @ @ \n" +" 8 tX: :8X@@@@88@@XS%%:;S%%%%t : 8 . \n" +" 8 8: 88 SSXX@@8@8 .t%%%%t 8 X\n" +" t@XX ; ;8 SSXX@@:S8X%%%%%%%t ; \n" +" 888@S ;8 88XSS88S:S@@88@S8t%%%%8 % :\n" +"X8888% 8 @S tSSX@@8. .%%%%% t X\n" +"8@@88 :@@X 8 .:%SSXX% @XS%% ; : \n" +"8@@@8 :888@X 8 :: %SS% @8@XS t ; \n" +"@@@@8% .8888@8X :8 X. 8@@8@ S. @ \n" +" @@@@8 t888888:8t8X8@@t :X 8SXX@8 8 8 \n" +" %@@@@: t;888888@XX @X S; SS@ X. .@ \n" +" 8@@@@8; S%888888@@X 8S. .@: ; ; :; \n" +" t@@@@X;; %:8X@t8;S88tS .88 @ t t \n" +" X@@@@88tt :8 X .S 8 @ \n" +" X@@@@@@8:8t....t88:@X ;@ 8 8 \n" +" 8X@@@@@@@@@X@888888t8 \n" +"  X888@@888;S \n"; From 398f6bf984f235e3626d03a4807c32a0d8cb9079 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 16:52:59 +0300 Subject: [PATCH 04/12] update client version to 3.0.0 (bump client-version) https://github.com/KomodoPlatform/komodo/commit/11316beaa4ad46b08929ceedb98f025efc5274d0 --- src/clientversion.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/clientversion.h b/src/clientversion.h index 16d10ded..e27b932d 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -30,10 +30,10 @@ */ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it -#define CLIENT_VERSION_MAJOR 2 +#define CLIENT_VERSION_MAJOR 3 #define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 15 -#define CLIENT_VERSION_BUILD 26 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true From a32b82e98cbb7fdb84353ef8e6824f98bb256297 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 17:27:56 +0300 Subject: [PATCH 05/12] bump protocol and KMD version - https://github.com/KomodoPlatform/komodo/commit/716eb32928d4a6833a86f55f33be7e87fc4c0353 - https://github.com/KomodoPlatform/komodo/commit/0e87481c2c29e16e73f9d4b6f43b217f1d6ad638 --- src/rpc/misc.cpp | 4 ++-- src/version.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 777e6bfc..03d7aca4 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -78,7 +78,7 @@ int8_t StakedNotaryID(std::string ¬aryname, char *Raddress); uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount); int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); -#define KOMODO_VERSION "0.4.0a" +#define KOMODO_VERSION "0.5.0" #define VERUS_VERSION "0.4.0g" extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAINS_CC; diff --git a/src/version.h b/src/version.h index f2fbbac0..c9c3bbc5 100644 --- a/src/version.h +++ b/src/version.h @@ -24,7 +24,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 170007; +static const int PROTOCOL_VERSION = 170008; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -33,7 +33,7 @@ static const int INIT_PROTO_VERSION = 209; static const int GETHEADERS_VERSION = 31800; //! disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION = 170002; +static const int MIN_PEER_PROTO_VERSION = 170007; static const int STAKEDMIN_PEER_PROTO_VERSION = 170007; //! nTime field added to CAddress, starting with this version; From 4c80e78094559f2e626f255ade020af2ad19828d Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 23:02:29 +0300 Subject: [PATCH 06/12] move opret check into CheckBlock (invalid typecast fix) in common case we can't obtain CBlock from CBlockHeader, so it's not the right place to do this check. --- src/main.cpp | 16 ++++++++++++++++ src/pow.cpp | 7 +++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 869d090a..635db70b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5122,6 +5122,22 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if ( ASSETCHAINS_STAKED == 0 && komodo_checkPOW(0,1,(CBlock *)&block,height) < 0 ) // checks Equihash return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW"); } + + if ( height > nDecemberHardforkHeight && ASSETCHAINS_SYMBOL[0] == 0 ) // December 2019 hardfork + { + int32_t notaryid; + int32_t special = komodo_chosennotary(¬aryid,height,pubkey33,tiptime); + if (notaryid > 0) { + CScript merkleroot = CScript(); + CBlock blockcopy = block; // block shouldn't be changed below, so let's make it's copy + CBlock *pblockcopy = (CBlock *)&blockcopy; + if (!komodo_checkopret(pblockcopy, merkleroot)) { + LogPrintf("failed or missing merkleroot expected.%s != merkleroot.%s\n", komodo_makeopret(pblockcopy, false).ToString().c_str(), merkleroot.ToString().c_str()); + return state.DoS(100, error("CheckBlock: failed or missing merkleroot opret in easy-mined"),REJECT_INVALID, "failed-merkle-opret-in-easy-mined"); + } + } + } + // Check the merkle root. if (fCheckMerkleRoot) { bool mutated; diff --git a/src/pow.cpp b/src/pow.cpp index b064c91a..14d8ce32 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * + * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * @@ -442,14 +442,17 @@ bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t } if ( (flag != 0 || special2 > 0) && special2 != -2 ) { + //LogPrintf("EASY MINING ht.%d\n",height); bnTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); + /* const void* pblock = &blkHeader; CScript merkleroot = CScript(); if ( height > nDecemberHardforkHeight && !komodo_checkopret((CBlock*)pblock, merkleroot) ) // December 2019 hardfork { - LogPrintf( "failed or missing expected.%s != %s\n", komodo_makeopret((CBlock*)pblock, false).ToString().c_str(), merkleroot.ToString().c_str()); + LogPrintf("failed or missing merkleroot expected.%s != merkleroot.%s\n", komodo_makeopret((CBlock*)pblock, false).ToString().c_str(), merkleroot.ToString().c_str()); return false; } + */ } } } From d9ed28b3dd5efd24f31113ebbe88117635f5bd36 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 28 Nov 2019 23:11:17 +0300 Subject: [PATCH 07/12] version bump in configure.ac --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 7fec160b..6022e588 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) -define(_CLIENT_VERSION_MAJOR, 2) +define(_CLIENT_VERSION_MAJOR, 3) define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 15) -define(_CLIENT_VERSION_BUILD, 26) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 0) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) From 2cdf469ac2c3d4b49352112f68f42cf1bccbb94d Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Fri, 29 Nov 2019 10:45:37 +0300 Subject: [PATCH 08/12] + add nsis installer configuration [win64] --- share/nsi_win64/content/komodo.conf | 14 ++++ share/nsi_win64/install.nsi | 97 ++++++++++++++++++++++++++++ share/nsi_win64/komodo.ico | Bin 0 -> 60105 bytes share/nsi_win64/readme.txt | 5 ++ 4 files changed, 116 insertions(+) create mode 100644 share/nsi_win64/content/komodo.conf create mode 100644 share/nsi_win64/install.nsi create mode 100644 share/nsi_win64/komodo.ico create mode 100644 share/nsi_win64/readme.txt diff --git a/share/nsi_win64/content/komodo.conf b/share/nsi_win64/content/komodo.conf new file mode 100644 index 00000000..112c8eb2 --- /dev/null +++ b/share/nsi_win64/content/komodo.conf @@ -0,0 +1,14 @@ +txindex=1 +addnode=5.9.102.210 +addnode=78.47.196.146 +addnode=178.63.69.164 +addnode=88.198.65.74 +addnode=5.9.122.241 +addnode=144.76.94.38 +addnode=89.248.166.91 +onlynet=ipv4 +rpcuser=komodo +rpcpassword=local321 +rpcallowip=127.0.0.1 +rpcbind=127.0.0.1 +server=1 diff --git a/share/nsi_win64/install.nsi b/share/nsi_win64/install.nsi new file mode 100644 index 00000000..bc26de0d --- /dev/null +++ b/share/nsi_win64/install.nsi @@ -0,0 +1,97 @@ +Target x86-unicode +; SetCompressor lzma + +; Settings +Name "KomodoOcean (Komodo-Qt)" +OutFile "komodo-qt-install.exe" +RequestExecutionLevel admin +InstallDir "$PROGRAMFILES64\KomodoOcean" +Icon "komodo.ico" +CRCCheck on + +; Includes + +; Pages +Page components +Page directory +Page instfiles + +;-------------------------------- + +Section "Install Komodo-Qt (GUI)" Section1 + SetOutPath $INSTDIR + File "content\komodo-qt-win.exe" + CreateShortCut "$DESKTOP\Komodo-Qt.lnk" "$INSTDIR\komodo-qt-win.exe" +SectionEnd + +Section "Download ZCash Params" Section3 + +SetOverwrite on +CreateDirectory "$APPDATA\ZcashParams" +DetailPrint "Checking: sprout-proving.key" +IfFileExists "$APPDATA\ZcashParams\sprout-proving.key" next_1 +DetailPrint "Downloading: sprout-proving.key" +inetc::get /CAPTION "ZCash Params" /POPUP "sprout-proving.key" \ + "https://z.cash/downloads/sprout-proving.key" "$APPDATA\ZcashParams\sprout-proving.key" \ + /END +Pop $0 +StrCmp $0 "OK" next_1 +MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to fetch ZCash params, click OK to abort installation" /SD IDOK +Abort +next_1: + +DetailPrint "Checking: sprout-verifying.key" +IfFileExists "$APPDATA\ZcashParams\sprout-verifying.key" next_2 +DetailPrint "Downloading: sprout-verifying.key" +inetc::get /CAPTION "ZCash Params" /POPUP "sprout-verifying.key" \ + "https://z.cash/downloads/sprout-verifying.key" "$APPDATA\ZcashParams\sprout-verifying.key" \ + /END +Pop $0 +StrCmp $0 "OK" next_2 +MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to fetch ZCash params, click OK to abort installation" /SD IDOK +Abort +next_2: + +DetailPrint "Checking: sapling-spend.params" +IfFileExists "$APPDATA\ZcashParams\sapling-spend.params" next_3 +DetailPrint "Downloading: sapling-spend.params" +inetc::get /CAPTION "ZCash Params" /POPUP "sapling-spend.params" \ + "https://z.cash/downloads/sapling-spend.params" "$APPDATA\ZcashParams\sapling-spend.params" \ + /END +Pop $0 +StrCmp $0 "OK" next_3 +MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to fetch ZCash params, click OK to abort installation" /SD IDOK +Abort +next_3: + +DetailPrint "Checking: sapling-output.params" +IfFileExists "$APPDATA\ZcashParams\sapling-output.params" next_4 +DetailPrint "Downloading: sapling-output.params" +inetc::get /CAPTION "ZCash Params" /POPUP "sapling-output.params" \ + "https://z.cash/downloads/sapling-output.params" "$APPDATA\ZcashParams\sapling-output.params" \ + /END +Pop $0 +StrCmp $0 "OK" next_4 +MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to fetch ZCash params, click OK to abort installation" /SD IDOK +Abort +next_4: + +DetailPrint "Checking: sprout-groth16.params" +IfFileExists "$APPDATA\ZcashParams\sprout-groth16.params" next_5 +DetailPrint "Downloading: sprout-groth16.params" +inetc::get /CAPTION "ZCash Params" /POPUP "sprout-groth16.params" \ + "https://z.cash/downloads/sprout-groth16.params" "$APPDATA\ZcashParams\sprout-groth16.params" \ + /END +Pop $0 +StrCmp $0 "OK" next_5 +MessageBox MB_OK|MB_ICONEXCLAMATION "Failed to fetch ZCash params, click OK to abort installation" /SD IDOK +Abort +next_5: +SectionEnd + +Section "Create komodo.conf" Section2 + SetOverwrite on + CreateDirectory "$APPDATA\Komodo" + SetOutPath $APPDATA\Komodo + File "content\komodo.conf" +SectionEnd \ No newline at end of file diff --git a/share/nsi_win64/komodo.ico b/share/nsi_win64/komodo.ico new file mode 100644 index 0000000000000000000000000000000000000000..33cfc44c80297d390924a56ad6d71f80bcbe0cc7 GIT binary patch literal 60105 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Ε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 R5Htt_4H+_W|GytE_g~uN!OQ>v literal 0 HcmV?d00001 diff --git a/share/nsi_win64/readme.txt b/share/nsi_win64/readme.txt new file mode 100644 index 00000000..e1f4a839 --- /dev/null +++ b/share/nsi_win64/readme.txt @@ -0,0 +1,5 @@ +Requirements: + +- Internet client plug-in for files download and upload (http://nsis.sourceforge.net/Inetc_plug-in) +- Place komodo-qt-win.exe in content\komodo-qt-win.exe, before create installer + From be64b943ef1f0ff0f6cfe502a56a632493896ec8 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Tue, 3 Dec 2019 23:01:22 +0300 Subject: [PATCH 09/12] libgmp, libsodium, libsnark versions and fetch urls - https://github.com/KomodoPlatform/komodo/commit/9d1af22230c33feb2cba18d2bcf18681d13cbf8a - https://github.com/KomodoPlatform/komodo/commit/629692e375ffc0dacde41d5158f020bbf61afbe2 - https://github.com/KomodoPlatform/komodo/commit/b8212b161c770ac85f1ada3b1c7c09b31dbe3918 --- depends/packages/libevent.mk | 2 +- depends/packages/libgmp.mk | 6 +++--- depends/packages/libsnark.mk | 8 ++++---- depends/packages/libsodium.mk | 15 +++++++++++---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 2a1125fd..ffe6f7e7 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,6 +1,6 @@ package=libevent $(package)_version=2.1.8 -$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_download_path=https://github.com/libevent/libevent/archive $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_download_file=release-$($(package)_version)-stable.tar.gz $(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d diff --git a/depends/packages/libgmp.mk b/depends/packages/libgmp.mk index f06e4a6c..6d948c2a 100644 --- a/depends/packages/libgmp.mk +++ b/depends/packages/libgmp.mk @@ -1,7 +1,7 @@ package=libgmp ifeq ($(host_os),mingw32) -$(package)_download_path=https://github.com/joshuayabut/$(package)/archive/ +$(package)_download_path=https://github.com/joshuayabut/$(package)/archive $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=193836c1acc9dc00fe2521205d7bbe1ba13263f6cbef6f02584bf6f8b34b108f @@ -9,7 +9,7 @@ $(package)_git_commit=053c03b1cab347671d936f43ef66b48ab5e380ee $(package)_dependencies= $(package)_config_opts=--enable-cxx --disable-shared else ifeq ($(build_os),darwin) -$(package)_download_path=https://github.com/ca333/$(package)/archive/ +$(package)_download_path=https://github.com/ca333/$(package)/archive $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=59b2c2b5d58fdf5943bfde1fa709e9eb53e7e072c9699d28dc1c2cbb3c8cc32c @@ -18,7 +18,7 @@ $(package)_dependencies= $(package)_config_opts=--enable-cxx --disable-shared else $(package)_version=6.1.1 -$(package)_download_path=https://gmplib.org/download/gmp/ +$(package)_download_path=https://ftp.gnu.org/gnu/gmp $(package)_file_name=gmp-$($(package)_version).tar.bz2 $(package)_sha256_hash=a8109865f2893f1373b0a8ed5ff7429de8db696fc451b1036bd7bdf95bbeffd6 $(package)_dependencies= diff --git a/depends/packages/libsnark.mk b/depends/packages/libsnark.mk index 00c897d7..9d886099 100644 --- a/depends/packages/libsnark.mk +++ b/depends/packages/libsnark.mk @@ -1,6 +1,6 @@ package=libsnark $(package)_version=0.1 -$(package)_download_path=https://supernetorg.bintray.com/misc/ +$(package)_download_path=https://github.com/ca333/libsnark/releases/download/v$($(package)_version)-$($(package)_git_commit)/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$(package)-$($(package)_git_commit).tar.gz $(package)_sha256_hash=47478adc2ae88c448dc736d59dfe007de6478e41e88d2d4d2ff4135a17ee6f90 @@ -14,15 +14,15 @@ define $(package)_set_vars $(package)_build_env+=CXXFLAGS="$($(package)_cxxflags) -DBINARY_OUTPUT -DSTATICLIB -DNO_PT_COMPRESSION=1 " endef define $(package)_build_cmds - $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64" + $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " endef else ifeq ($(host_os),mingw32) define $(package)_build_cmds - CXX="x86_64-w64-mingw32-g++-posix" CXXFLAGS="-DBINARY_OUTPUT -DPTW32_STATIC_LIB -DSTATICLIB -DNO_PT_COMPRESSION=1 -fopenmp" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64" + CXX="x86_64-w64-mingw32-g++-posix" CXXFLAGS="-DBINARY_OUTPUT -DPTW32_STATIC_LIB -DSTATICLIB -DNO_PT_COMPRESSION=1 -fopenmp" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " endef else define $(package)_build_cmds - CXXFLAGS="-fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64" + CXXFLAGS="-fPIC -DBINARY_OUTPUT -DNO_PT_COMPRESSION=1" $(MAKE) lib DEPINST=$(host_prefix) CURVE=ALT_BN128 MULTICORE=1 NO_PROCPS=1 NO_GTEST=1 NO_DOCS=1 STATIC=1 NO_SUPERCOP=1 FEATUREFLAGS=-DMONTGOMERY_OUTPUT OPTFLAGS="-O2 -march=x86-64 -g " endef endif diff --git a/depends/packages/libsodium.mk b/depends/packages/libsodium.mk index 37122501..a80815d1 100644 --- a/depends/packages/libsodium.mk +++ b/depends/packages/libsodium.mk @@ -1,10 +1,17 @@ package=libsodium -$(package)_version=1.0.11 -$(package)_download_path=https://supernetorg.bintray.com/misc -$(package)_file_name=libsodium-1.0.11.tar.gz -$(package)_sha256_hash=a14549db3c49f6ae2170cbbf4664bd48ace50681045e8dbea7c8d9fb96f9c765 +$(package)_version=1.0.18 +$(package)_download_path=https://download.libsodium.org/libsodium/releases +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1 $(package)_dependencies= $(package)_config_opts= +ifeq ($(build_os),darwin) +define $(package)_set_vars + $(package)_build_env=MACOSX_DEPLOYMENT_TARGET="10.11" + $(package)_cc=clang + $(package)_cxx=clang++ +endef +endif define $(package)_preprocess_cmds cd $($(package)_build_subdir); ./autogen.sh From b4f3bb1594ffdc23fa577cf39b406965974d2df5 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Wed, 4 Dec 2019 02:07:31 +0300 Subject: [PATCH 10/12] libsodium: prevent update config.sub and config.guess from git.savannah.gnu.org --- depends/packages/libsodium.mk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/depends/packages/libsodium.mk b/depends/packages/libsodium.mk index a80815d1..4974edc7 100644 --- a/depends/packages/libsodium.mk +++ b/depends/packages/libsodium.mk @@ -5,13 +5,15 @@ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1 $(package)_dependencies= $(package)_config_opts= -ifeq ($(build_os),darwin) + define $(package)_set_vars - $(package)_build_env=MACOSX_DEPLOYMENT_TARGET="10.11" + $(package)_build_env=DO_NOT_UPDATE_CONFIG_SCRIPTS=1 + ifeq ($(build_os),darwin) + $(package)_build_env+=MACOSX_DEPLOYMENT_TARGET="10.11" $(package)_cc=clang $(package)_cxx=clang++ + endif endef -endif define $(package)_preprocess_cmds cd $($(package)_build_subdir); ./autogen.sh From 4a3f738a6975a4c3b49bb03447e6095e332b4254 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Wed, 4 Dec 2019 04:01:32 +0300 Subject: [PATCH 11/12] add boost and sodium versions in log at startup --- src/init.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 0031978e..da4720a1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1283,7 +1283,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #endif if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); - LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); + //LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("Komodo version %s (%s)\n", FormatFullVersion(), CLIENT_DATE); if (fPrintToDebugLog) @@ -1292,6 +1292,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef ENABLE_WALLET LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); #endif + std::stringstream boost_version_ss(""); + boost_version_ss << BOOST_VERSION / 100000 << "." // major version + << BOOST_VERSION / 100 % 1000 << "." // minor version + << BOOST_VERSION % 100; // patch level + LogPrintf("Using Boost version %s\n", boost_version_ss.str() /*BOOST_LIB_VERSION*/ ); + LogPrintf("Using Sodium version %s\n", sodium_version_string()); + if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); From 379954fe82e7cfb8943f22414981a6e7ca08a41a Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Wed, 4 Dec 2019 16:17:41 +0300 Subject: [PATCH 12/12] bump boost 1.62 -> 1.66 also this commit should fix link errors in some cases, bcz of obtain boost_system lib name from $BOOST_SYSTEM_LIB. --- configure.ac | 16 +++++++++------- depends/packages/boost.mk | 19 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 6022e588..37d9a221 100644 --- a/configure.ac +++ b/configure.ac @@ -877,13 +877,15 @@ AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"]) AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"]) AX_CHECK_COMPILE_FLAG([-Wno-builtin-declaration-mismatch],[CXXFLAGS="$CXXFLAGS -Wno-builtin-declaration-mismatch"],,[[$CXXFLAG_WERROR]]) -if test x$TARGET_OS = xdarwin; then - LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium $RUST_LIBS" -elif test x$TARGET_OS != xwindows; then - LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system -lcrypto -lsodium $RUST_LIBS" -else - LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system-mt-s -lcrypto -lsodium $RUST_LIBS" -fi +# if test x$TARGET_OS = xdarwin; then +# LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium $RUST_LIBS" +# elif test x$TARGET_OS != xwindows; then +# LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system -lcrypto -lsodium $RUST_LIBS" +# else +# LIBZCASH_LIBS="-lgmp -lgmpxx -lboost_system-mt-s -lcrypto -lsodium $RUST_LIBS" +# fi + +LIBZCASH_LIBS="-lgmp -lgmpxx $BOOST_SYSTEM_LIB -lcrypto -lsodium $RUST_LIBS" CXXFLAGS_TEMP="$CXXFLAGS" LIBS_TEMP="$LIBS" diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 679faacd..800c424c 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,17 +1,17 @@ package=boost -$(package)_version=1_62_0 -$(package)_download_path=http://sourceforge.net/projects/boost/files/boost/1.62.0 + +$(package)_version=1_66_0 +$(package)_download_path=https://dl.bintray.com/boostorg/release/1.66.0/source +$(package)_sha256_hash=5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9 $(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=36c96b0f6155c98404091d8ceb48319a28279ca0333fba1ad8611eb90afb2ca0 -$(package)_patches=deprecated_auto_ptr.patch include_poll.patch define $(package)_set_vars $(package)_config_opts_release=variant=release $(package)_config_opts_debug=variant=debug -$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam +$(package)_config_opts=--layout=system --user-config=user-config.jam $(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 $(package)_config_opts_linux=threadapi=pthread runtime-link=shared -$(package)_config_opts_darwin=--toolset=gcc threadapi=pthread runtime-link=shared +$(package)_config_opts_darwin=--toolset=gcc runtime-link=shared threadapi=pthread $(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static $(package)_config_opts_x86_64_mingw32=address-model=64 $(package)_config_opts_i686_mingw32=address-model=32 @@ -21,14 +21,13 @@ $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=gcc $(package)_archiver_darwin=$($(package)_ar) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test -$(package)_cxxflags=-fvisibility=hidden +$(package)_cxxflags=-std=c++11 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC endef + define $(package)_preprocess_cmds - echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam && \ - patch -p1 < $($(package)_patch_dir)/deprecated_auto_ptr.patch && \ - patch -p1 < $($(package)_patch_dir)/include_poll.patch + echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef define $(package)_config_cmds