diff --git a/depends/packages/libgmp.mk b/depends/packages/libgmp.mk index f06e4a6c4..c10cb36c9 100644 --- a/depends/packages/libgmp.mk +++ b/depends/packages/libgmp.mk @@ -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/pub/gnu/gmp $(package)_file_name=gmp-$($(package)_version).tar.bz2 $(package)_sha256_hash=a8109865f2893f1373b0a8ed5ff7429de8db696fc451b1036bd7bdf95bbeffd6 $(package)_dependencies= diff --git a/src/chainparams.cpp b/src/chainparams.cpp index af943aba6..be9d7ac8a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -80,12 +80,9 @@ void *chainparams_commandline(void *ptr); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; -extern uint32_t ASSETCHAIN_INIT; -extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_SUPPLY; -extern uint64_t ASSETCHAINS_ALGO; -extern uint64_t ASSETCHAINS_EQUIHASH; -extern uint64_t ASSETCHAINS_VERUSHASH; +extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC; +extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS; +extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH; const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); @@ -244,6 +241,18 @@ void *chainparams_commandline(void *ptr) mainParams.consensus.powAlternate = uint256S("00000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); } + if (ASSETCHAINS_LWMAPOS != 0) + { + mainParams.consensus.posLimit = uint256S("000000000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); + mainParams.consensus.nPOSAveragingWindow = 45; + // spacing is 1000 units per block to get better resolution, POS is 50% hard coded for now, we can vary it later + // when we get reliable integer math on nLwmaPOSAjustedWeight + mainParams.consensus.nPOSTargetSpacing = VERUS_BLOCK_POSUNITS * 2; + // nLwmaPOSAjustedWeight = (N+1)/2 * (0.9989^(500/nPOSAveragingWindow)) * nPOSTargetSpacing + // this needs to be recalculated if VERUS_BLOCK_POSUNITS is changed + mainParams.consensus.nLwmaPOSAjustedWeight = 45000; + } + checkpointData = //(Checkpoints::CCheckpointData) { boost::assign::map_list_of diff --git a/src/consensus/params.h b/src/consensus/params.h index c901547ae..61c35c837 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -97,11 +97,17 @@ struct Params { int64_t nPowMaxAdjustDown; int64_t nPowMaxAdjustUp; int64_t nPowTargetSpacing; - int64_t nMaxFutureBlockTime; - - // Verus algorithm's lwma difficulty int64_t nLwmaAjustedWeight; + /* Proof of stake parameters */ + uint256 posLimit; + int64_t nPOSAveragingWindow; // can be completely different than POW and initially trying a relatively large number, like 100 + int64_t nPOSTargetSpacing; // spacing is 1000 units per block to get better resolution, (100 % = 1000, 50% = 2000, 10% = 10000) + int64_t nLwmaPOSAjustedWeight; + + /* applied to all block times */ + int64_t nMaxFutureBlockTime; + int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; } int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; } int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; } diff --git a/src/crypto/haraka.h b/src/crypto/haraka.h index ab56424ac..203f8fbf6 100644 --- a/src/crypto/haraka.h +++ b/src/crypto/haraka.h @@ -30,8 +30,8 @@ Optimized Implementations for Haraka256 and Haraka512 #define NUMROUNDS 5 -#define u64 unsigned long -#define u128 __m128i +typedef unsigned long u64; +typedef __m128i u128; extern u128 rc[40]; diff --git a/src/crypto/verus_hash.h b/src/crypto/verus_hash.h index 394a5e070..1d9b0b4c7 100644 --- a/src/crypto/verus_hash.h +++ b/src/crypto/verus_hash.h @@ -31,7 +31,6 @@ class CVerusHash result = buf2; curPos = 0; std::fill(buf1, buf1 + sizeof(buf1), 0); - std::fill(buf2, buf2 + sizeof(buf2), 0); } void Finalize(unsigned char hash[32]) @@ -42,7 +41,7 @@ class CVerusHash haraka512(hash, curBuf); } else - std::memcpy(hash, result, 32); + std::memcpy(hash, curBuf, 32); } private: diff --git a/src/fiat/verus b/src/fiat/verus index e0e37ab96..0bcc79fd2 100644 --- a/src/fiat/verus +++ b/src/fiat/verus @@ -1,2 +1,7 @@ #!/bin/bash -./komodo-cli -ac_name=VERUSTEST "$@" + +#set working directory to the location of this script +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR + +../komodo-cli -ac_name=VERUSTEST "$@" diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 359f88e8b..d42a8f9e6 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -27,6 +27,7 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_electednotary(int32_t *numnotariesp,uint8_t *pubkey33,int32_t height,uint32_t timestamp); +unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); //#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr)) @@ -557,7 +558,7 @@ uint64_t komodo_seed(int32_t height) return(seed); } -uint32_t komodo_txtime(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) +uint32_t komodo_txtime(uint64_t *valuep,uint256 hash, int32_t n, char *destaddr) { CTxDestination address; CTransaction tx; uint256 hashBlock; *valuep = 0; @@ -666,18 +667,19 @@ int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block) else memset(pubkey33,0,33); if ( block->vtx[0].vout.size() > 0 ) { -#ifdef KOMODO_ZCASH - uint8_t *ptr = (uint8_t *)block->vtx[0].vout[0].scriptPubKey.data(); -#else - uint8_t *ptr = (uint8_t *)&block->vtx[0].vout[0].scriptPubKey[0]; -#endif - //komodo_init(0); - n = block->vtx[0].vout[0].scriptPubKey.size(); - if ( n == 35 ) + txnouttype whichType; + vector> vch = vector>(); + if (Solver(block->vtx[0].vout[0].scriptPubKey, whichType, vch) && whichType == TX_PUBKEY) { - memcpy(pubkey33,ptr+1,33); - return(1); + CPubKey pubKey(vch[0]); + if (pubKey.IsValid()) + { + memcpy(pubkey33,vch[0].data(),33); + return true; + } + else memset(pubkey33,0,33); } + else memset(pubkey33,0,33); } return(0); } @@ -1256,6 +1258,100 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ return(isPoS); } +// if slow flag is 1, this does a slower check that checks the target with consensus, otherwise quick, insecure check for internal integrity +bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) +{ + CBlockIndex *pastBlockIndex; + uint256 txid, blkHash; + int32_t txn_count; + uint32_t voutNum; + bool isPOS = false; + CTxDestination voutaddress, destaddress; + arith_uint256 target, hash; + CTransaction tx; + + if (!pblock->isVerusPOSBlock()) + return false; + + char voutaddr[64],destaddr[64]; + + target.SetCompact(pblock->GetVerusPOSTarget()); + txn_count = pblock->vtx.size(); + + if ( txn_count > 1 ) + { + txid = pblock->vtx[txn_count-1].vin[0].prevout.hash; + voutNum = pblock->vtx[txn_count-1].vin[0].prevout.n; + +#ifndef KOMODO_ZCASH + if (!GetTransaction(txid, tx, Params().GetConsensus(), blkHash, true)) +#else + if (!GetTransaction(txid, tx, blkHash, true)) +#endif + { + fprintf(stderr,"ERROR: invalid PoS block %s - no transaction\n",blkHash.ToString().c_str()); + } + else if (!(pastBlockIndex = komodo_chainactive(height - COINBASE_MATURITY))) + { + fprintf(stderr,"ERROR: invalid PoS block %s - no past block hash\n",blkHash.ToString().c_str()); + } + else + { + hash = UintToArith256(tx.GetVerusPOSHash(voutNum, height, pastBlockIndex->GetBlockHash())); + if (hash <= target) + { + if ((mapBlockIndex.count(blkHash) == 0) || + !(pastBlockIndex = mapBlockIndex[blkHash]) || + (height - pastBlockIndex->nHeight) < VERUS_MIN_STAKEAGE) + { + fprintf(stderr,"ERROR: invalid PoS block %s - no prev block found\n",blkHash.ToString().c_str()); + } + else if ( slowflag != 0 ) + { + // make sure we have the right target + CBlockIndex *previndex; + if (!(previndex = mapBlockIndex[pblock->hashPrevBlock])) + { + fprintf(stderr,"ERROR: invalid PoS block %s - no prev block found\n",blkHash.ToString().c_str()); + } + else + { + arith_uint256 cTarget; + cTarget.SetCompact(lwmaGetNextPOSRequired(previndex, Params().GetConsensus())); + + if (cTarget != target) + { + fprintf(stderr,"ERROR: invalid PoS block %s - invalid diff target\n",blkHash.ToString().c_str()); + } + else if ( ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) && + ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) ) + { + strcpy(voutaddr, CBitcoinAddress(voutaddress).ToString().c_str()); + strcpy(destaddr, CBitcoinAddress(destaddress).ToString().c_str()); + if ( strcmp(destaddr,voutaddr) == 0 ) + { + isPOS = true; + } + else + { + fprintf(stderr,"ERROR: invalid PoS block %s - invalid stake destination\n",blkHash.ToString().c_str()); + } + } + } + } + else + { + // with fast check, we get true here, slow check ensures destination address + // of staking transaction matches original source and that the target + // matches the consensus target + isPOS = true; + } + } + } + } + return(isPOS); +} + int32_t komodo_checkPOW(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,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; @@ -1278,6 +1374,14 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) if ( height == 0 ) return(0); } + if ( ASSETCHAINS_LWMAPOS != 0 && bhash > bnTarget ) + { + // if proof of stake is active, check if this is a valid PoS block before we fail + if (verusCheckPOSBlock(slowflag, pblock, height)) + { + return(0); + } + } if ( (ASSETCHAINS_SYMBOL[0] != 0 || height > 792000) && bhash > bnTarget ) { failed = 1; diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 7f70c814e..8663a0c0a 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -78,6 +78,11 @@ uint32_t ASSETCHAINS_NONCESHIFT[] = {32,40}; uint32_t ASSETCHAINS_HASHESPERROUND[] = {1,512}; uint32_t ASSETCHAINS_ALGO = _ASSETCHAINS_EQUIHASH; +// Verus proof of stake controls +int32_t ASSETCHAINS_LWMAPOS = 0; // percentage of blocks should be PoS +int32_t VERUS_BLOCK_POSUNITS = 1000; // one block is 1000 units +int32_t VERUS_MIN_STAKEAGE = 150; // 1/2 this should also be a cap on the POS averaging window, or startup could be too easy + uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index fa03f66aa..2fa46819a 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1020,7 +1020,7 @@ int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_ // get a pseudo random number that is the same for each block individually at all times and different // from all other blocks. the sequence is extremely likely, but not guaranteed to be unique for each block chain -uint64_t blockPRG(uint32_t nHeight) +uint64_t komodo_block_prg(uint32_t nHeight) { int i; uint8_t hashSrc[8]; @@ -1052,7 +1052,7 @@ int64_t komodo_block_unlocktime(uint32_t nHeight) unlocktime = ASSETCHAINS_TIMEUNLOCKTO; else { - unlocktime = blockPRG(nHeight) / (0xffffffffffffffff / ((ASSETCHAINS_TIMEUNLOCKTO - ASSETCHAINS_TIMEUNLOCKFROM) + 1)); + unlocktime = komodo_block_prg(nHeight) / (0xffffffffffffffff / ((ASSETCHAINS_TIMEUNLOCKTO - ASSETCHAINS_TIMEUNLOCKFROM) + 1)); // boundary and power of 2 can make it exceed to time by 1 unlocktime = unlocktime + ASSETCHAINS_TIMEUNLOCKFROM; if (unlocktime > ASSETCHAINS_TIMEUNLOCKTO) @@ -1602,6 +1602,12 @@ uint64_t komodo_ac_block_subsidy(int nHeight) } } } + if ( nHeight == 1 ) + if ( ASSETCHAINS_LASTERA == 0 ) + subsidy = ASSETCHAINS_SUPPLY * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff); + else + subsidy += ASSETCHAINS_SUPPLY * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff); + return(subsidy); } @@ -1701,6 +1707,8 @@ void komodo_args(char *argv0) ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey",""); if ( (ASSETCHAINS_STAKED= GetArg("-ac_staked",0)) > 100 ) ASSETCHAINS_STAKED = 100; + if ( (ASSETCHAINS_LWMAPOS = GetArg("-ac_veruspos",0)) > 100 ) + ASSETCHAINS_LWMAPOS = 100; if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 && ASSETCHAINS_COMMISSION > 0 && ASSETCHAINS_COMMISSION <= 100000000 ) decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str()); @@ -1752,6 +1760,11 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_ALGO),(void *)&ASSETCHAINS_ALGO); } + if ( ASSETCHAINS_LWMAPOS != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_LWMAPOS),(void *)&ASSETCHAINS_LWMAPOS); + } + val = ASSETCHAINS_COMMISSION | (((uint64_t)ASSETCHAINS_STAKED & 0xff) << 32) | (((uint64_t)ASSETCHAINS_CC & 0xffffff) << 40); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(val),(void *)&val); } diff --git a/src/main.cpp b/src/main.cpp index 3408647d3..d3b0c1d7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1731,7 +1731,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos,bo if ( 0 && checkPOW != 0 ) { komodo_block2pubkey33(pubkey33,(CBlock *)&block); - if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus(),block.nTime))) + if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(block, pubkey33, height, Params().GetConsensus()))) { int32_t i; for (i=0; i<33; i++) fprintf(stderr,"%02x",pubkey33[i]); @@ -1774,10 +1774,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) } else { - if ( nHeight == 1 ) - return(ASSETCHAINS_SUPPLY * COIN + (ASSETCHAINS_MAGIC & 0xffffff)); - else - return(komodo_ac_block_subsidy(nHeight)); + return(komodo_ac_block_subsidy(nHeight)); } /* // Mining slow start @@ -2822,7 +2819,7 @@ 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); } - + view.PushAnchor(tree); if (!fJustCheck) { pindex->hashAnchorEnd = tree.root(); @@ -3328,6 +3325,13 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + + // asset chains cannot reorg past block 1, which is specific to the asset chain + if (ASSETCHAINS_SYMBOL[0] != 0 && pindexFork && pindexFork->nHeight < 1 && chainActive.Tip()->nHeight != pindexFork->nHeight) + { + LogPrintf("Failed attempt to reorg past block 1 in asset chain %s\n", ASSETCHAINS_SYMBOL); + return false; + } // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, @@ -3847,10 +3851,10 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C } if ( fCheckPOW ) { - //if ( !CheckEquihashSolution(&block, Params()) ) + //if ( !CheckEquihashSolution(&block, Params()) ) // return state.DoS(100, error("CheckBlock: Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); komodo_block2pubkey33(pubkey33,(CBlock *)&block); - if ( !CheckProofOfWork(height,pubkey33,hash,block.nBits,Params().GetConsensus(),block.nTime) ) + if ( !CheckProofOfWork(block,pubkey33,height,Params().GetConsensus()) ) { int32_t z; for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&hash)[z]); @@ -3933,7 +3937,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta assert(pindexPrev); int nHeight = pindexPrev->nHeight+1; - + // Check proof of work if ( (ASSETCHAINS_SYMBOL[0] != 0 || nHeight < 235300 || nHeight > 236000) && block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { @@ -3997,6 +4001,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); + bool checkBlockOne = (nHeight == 1); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -4005,7 +4010,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { return false; // Failure reason has been set in validation state object } - + int nLockTimeFlags = 0; int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) ? pindexPrev->GetMedianTimePast() @@ -4027,7 +4032,6 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); } } - return true; } diff --git a/src/miner.cpp b/src/miner.cpp index ac2b70e58..22d5d5a5b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -111,7 +111,7 @@ extern int32_t ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOS extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED; extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[]; extern const char *ASSETCHAINS_ALGORITHMS[]; -extern int32_t ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; +extern int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern std::string NOTARY_PUBKEY; extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33]; @@ -127,6 +127,7 @@ int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_ int64_t komodo_block_unlocktime(uint32_t nHeight); uint64_t komodo_commission(const CBlock *block); int32_t komodo_staked(CPubKey &pubkey, CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig); +int32_t verus_staked(CPubKey &pubkey, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, bool isStake) @@ -450,11 +451,25 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, bool isStake) } } - uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid,revtxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr; + uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr; CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1); + //if ( blocktime > pindexPrev->GetMedianTimePast()+60 ) // blocktime = pindexPrev->GetMedianTimePast() + 60; - if ( (siglen= komodo_staked(key, txStaked,pblock->nBits,&blocktime,&txtime,&utxotxid,&utxovout,&utxovalue,utxosig)) > 0 ) + if (ASSETCHAINS_LWMAPOS != 0) + { + uint32_t nBitsPOS; + arith_uint256 posHash; + siglen = verus_staked(key, txStaked, nBitsPOS, posHash, utxosig); + blocktime = GetAdjustedTime(); + pblock->SetVerusPOSTarget(nBitsPOS); + } + else + { + siglen = komodo_staked(key, txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig); + } + + if ( siglen > 0 ) { CAmount txfees = 0; if ( GetAdjustedTime() < blocktime-13 ) @@ -516,13 +531,17 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, bool isStake) pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; - // Randomise nonce - arith_uint256 nonce = UintToArith256(GetRandHash()); + // if not Verus stake, setup nonce, otherwise, leave it alone + if (!isStake || ASSETCHAINS_LWMAPOS == 0) + { + // Randomise nonce + arith_uint256 nonce = UintToArith256(GetRandHash()); - // Clear the top 16 and bottom 16 or 24 bits (for local use as thread flags and counters) - nonce <<= ASSETCHAINS_NONCESHIFT[ASSETCHAINS_ALGO]; - nonce >>= 16; - pblock->nNonce = ArithToUint256(nonce); + // Clear the top 16 and bottom 16 or 24 bits (for local use as thread flags and counters) + nonce <<= ASSETCHAINS_NONCESHIFT[ASSETCHAINS_ALGO]; + nonce >>= 16; + pblock->nNonce = ArithToUint256(nonce); + } // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); @@ -530,7 +549,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, bool isStake) if ( ASSETCHAINS_SYMBOL[0] == 0 || ASSETCHAINS_STAKED == 0 || NOTARY_PUBKEY33[0] == 0 ) { UpdateTime(pblock, Params().GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus()); } pblock->nSolution.clear(); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); @@ -566,7 +585,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, bool isStake) } } } - return pblocktemplate.release(); } @@ -802,22 +820,23 @@ void static VerusStaker(CWallet *pwallet) // try a nice clean peer connection to start waitForPeers(chainparams); sleep(5); - CBlockIndex* curTip; + CBlockIndex *curTip = chainActive.Tip(), *lastTip; do { + lastTip = curTip; + printf("Verifying block height %d \n", lastTip->nHeight); + MilliSleep(3000 + rand() % 1900); curTip = chainActive.Tip(); - printf("Verifying block height %d \n", chainActive.Tip()->nHeight); - MilliSleep(2000 + rand() % 1900); - } while (curTip != chainActive.Tip()); + } while (curTip != lastTip); SetThreadPriority(THREAD_PRIORITY_LOWEST); sleep(5); - printf("Staking height %d\n", chainActive.Tip()->nHeight + 1); + printf("Staking height %d for %s\n", chainActive.Tip()->nHeight + 1, ASSETCHAINS_SYMBOL); + //fprintf(stderr,"Staking height %d for %s\n", chainActive.Tip()->nHeight + 1, ASSETCHAINS_SYMBOL); miningTimer.start(); try { - fprintf(stderr,"Mining %s with %s\n", ASSETCHAINS_SYMBOL, ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]); while (true) { miningTimer.stop(); @@ -833,13 +852,19 @@ void static VerusStaker(CWallet *pwallet) Mining_start = (uint32_t)time(NULL); } + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // try to stake a block - CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, true); + CBlockTemplate *ptr = NULL; + if (Mining_height > VERUS_MIN_STAKEAGE) + ptr = CreateNewBlockWithKey(reservekey, true); if ( ptr == 0 ) { - // wait to try another staking block - sleep(5); + // wait to try another staking block until after the tip moves again + while ( chainActive.Tip() == pindexPrev ) + sleep(5); continue; } @@ -847,44 +872,43 @@ void static VerusStaker(CWallet *pwallet) if (!pblocktemplate.get()) { if (GetArg("-mineraddress", "").empty()) { - LogPrintf("Error in %s miner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n", + LogPrintf("Error in %s staker: Keypool ran out, please call keypoolrefill before restarting the mining thread\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]); } else { // Should never reach here, because -mineraddress validity is checked in init.cpp - LogPrintf("Error in %s miner: Invalid %s -mineraddress\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], ASSETCHAINS_SYMBOL); + LogPrintf("Error in %s staker: Invalid %s -mineraddress\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], ASSETCHAINS_SYMBOL); } return; } + CBlock *pblock = &pblocktemplate->block; - if ( ASSETCHAINS_SYMBOL[0] != 0 ) - { - if ( ASSETCHAINS_REWARD[0] == 0 && !ASSETCHAINS_LASTERA ) - { - if ( pblock->vtx.size() == 1 && pblock->vtx[0].vout.size() == 1 && Mining_height > ASSETCHAINS_MINHEIGHT ) - { - static uint32_t counter; - if ( counter++ < 10 ) - fprintf(stderr,"skip generating %s on-demand block, no tx avail\n",ASSETCHAINS_SYMBOL); - sleep(10); - continue; - } else fprintf(stderr,"%s vouts.%d mining.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pblock->vtx[0].vout.size(),Mining_height,ASSETCHAINS_MINHEIGHT); - } - } - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - LogPrintf("Running %s miner with %u transactions in block (%u bytes)\n",ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO], - pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION)); + LogPrintf("Staking with %u transactions in block (%u bytes)\n", pblock->vtx.size(),::GetSerializeSize(*pblock,SER_NETWORK,PROTOCOL_VERSION)); // // Search // - uint32_t savebits; int64_t nStart = GetTime(); + int64_t nStart = GetTime(); + + // we don't use this, but IncrementExtraNonce is the function that builds the merkle tree + unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); pblock->nSolution = solnPlaceholder; - savebits = pblock->nBits; - arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]); - Mining_start = 0; + if (vNodes.empty() && chainparams.MiningRequiresPeers()) + { + if ( Mining_height > ASSETCHAINS_MINHEIGHT ) + { + fprintf(stderr,"no nodes, attempting reconnect\n"); + continue; + } + } + + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + { + fprintf(stderr,"timeout, retrying\n"); + continue; + } if ( pindexPrev != chainActive.Tip() ) { @@ -893,91 +917,37 @@ void static VerusStaker(CWallet *pwallet) continue; } - int32_t percPoS,z; + SetThreadPriority(THREAD_PRIORITY_NORMAL); - while (true) - { - // for speed check multiples at a time - for (int i = 0; i < ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO]; i++) - { - solutionTargetChecks.increment(); + int32_t unlockTime = komodo_block_unlocktime(Mining_height); + int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue); - // Update nNonce and nTime - pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + uint256 hashTarget = ArithToUint256(arith_uint256().SetCompact(pblock->nBits)); - if ( UintToArith256(pblock->GetHash()) <= hashTarget ) - { - SetThreadPriority(THREAD_PRIORITY_NORMAL); + LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]); + LogPrintf("Staked block found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); + printf("Found block %d \n", Mining_height ); + printf("staking reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL); + printf(" hash: %s \ntarget: %s\n", pblock->GetHash().GetHex().c_str(), hashTarget.GetHex().c_str()); + if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE) + printf("- timelocked until block %i\n", unlockTime); + else + printf("\n"); - int32_t unlockTime = komodo_block_unlocktime(Mining_height); - int64_t subsidy = (int64_t)(pblock->vtx[0].vout[0].nValue); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - LogPrintf("Using %s algorithm:\n", ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO]); - LogPrintf("Staked block found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); - printf("Found block %d \n", Mining_height ); - printf("mining reward %.8f %s!\n", (double)subsidy / (double)COIN, ASSETCHAINS_SYMBOL); - printf(" hash: %s \ntarget: %s\n", pblock->GetHash().GetHex().c_str(), hashTarget.GetHex().c_str()); - if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE) - printf("- timelocked until block %i\n", unlockTime); - else - printf("\n"); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - ProcessBlockFound(pblock, *pwallet, reservekey); + ProcessBlockFound(pblock, *pwallet, reservekey); - SetThreadPriority(THREAD_PRIORITY_LOWEST); + sleep(3); + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + SetThreadPriority(THREAD_PRIORITY_LOWEST); - // In regression test mode, stop mining after a block is found. - if (chainparams.MineBlocksOnDemand()) { - throw boost::thread_interrupted(); - } - break; - } - else - { - if ((UintToArith256(pblock->nNonce) & mask) == mask) - { - break; - } - } - } - - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - - if (vNodes.empty() && chainparams.MiningRequiresPeers()) - { - if ( Mining_height > ASSETCHAINS_MINHEIGHT ) - { - fprintf(stderr,"no nodes, attempting reconnect\n"); - break; - } - } - - if ((UintToArith256(pblock->nNonce) & mask) == mask) - { - fprintf(stderr,"%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576); - break; - } - - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - { - fprintf(stderr,"timeout, retrying\n"); - break; - } - - if ( pindexPrev != chainActive.Tip() ) - { - printf("Block %d added to chain\n", chainActive.Tip()->nHeight); - break; - } - - pblock->nBits = savebits; - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) - { - // Changing pblock->nTime can change work required on testnet: - hashTarget.SetCompact(pblock->nBits); - } + // In regression test mode, stop mining after a block is found. + if (chainparams.MineBlocksOnDemand()) { + throw boost::thread_interrupted(); } } } @@ -1026,12 +996,13 @@ void static BitcoinMiner_noeq() // try a nice clean peer connection to start waitForPeers(chainparams); sleep(5); - CBlockIndex* curTip; + CBlockIndex *curTip = chainActive.Tip(), *lastTip; do { + lastTip = curTip; + printf("Verifying block height %d \n", lastTip->nHeight); + MilliSleep(3000 + rand() % 1900); curTip = chainActive.Tip(); - printf("Verifying block height %d \n", chainActive.Tip()->nHeight); - MilliSleep(2000 + rand() % 1900); - } while (curTip != chainActive.Tip()); + } while (curTip != lastTip); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -1478,7 +1449,7 @@ void static BitcoinMiner() // (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; - if ( NOTARY_PUBKEY33[0] == 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 ) + if ( NOTARY_PUBKEY33[0] == 0 && ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED <= 100 ) hashTarget = HASHTarget_POW; else hashTarget = HASHTarget; std::function)> validBlock = @@ -1726,13 +1697,13 @@ void static BitcoinMiner() minerThreads = NULL; } - if ((nThreads == 0 && ASSETCHAINS_STAKED == 0) || !fGenerate) + if ((nThreads == 0 && ASSETCHAINS_LWMAPOS == 0) || !fGenerate) return; minerThreads = new boost::thread_group(); #ifdef ENABLE_WALLET - if (ASSETCHAINS_STAKED != 0) + if (ASSETCHAINS_LWMAPOS != 0) { minerThreads->create_thread(boost::bind(&VerusStaker, pwallet)); } diff --git a/src/pow.cpp b/src/pow.cpp index 800ec7d2c..70319ca34 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -22,6 +22,7 @@ uint32_t komodo_chainactive_timestamp(); extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH; +extern int32_t VERUS_BLOCK_POSUNITS; unsigned int lwmaGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params); unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); @@ -142,6 +143,73 @@ unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const return nextTarget.GetCompact(); } +bool DoesHashQualify(const CBlockIndex *pbindex) +{ + // if it fails hash test and PoW validation, consider it POS. it could also be invalid + arith_uint256 hash = UintToArith256(pbindex->GetBlockHash()); + // to be considered POS, we first can't qualify as POW + if (hash > hash.SetCompact(pbindex->nBits)) + { + return false; + } + return true; +} + +// the goal is to keep POS at a solve time that is a ratio of block time units. the low resolution makes a stable solution more challenging +// and requires that the averaging window be quite long. +unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params) +{ + arith_uint256 nextTarget {0}, sumTarget {0}, bnTmp, bnLimit; + bnLimit = UintToArith256(params.posLimit); + unsigned int nProofOfStakeLimit = bnLimit.GetCompact(); + + // Find the first block in the averaging interval as we total the linearly weighted average + // of POS solve times + const CBlockIndex* pindexFirst = pindexLast; + const CBlockIndex* pindexNext; + + int64_t t = 0, solvetime = 0, k = params.nLwmaPOSAjustedWeight, N = params.nPOSAveragingWindow; + + for (int i = 0, j = N - 1; pindexFirst && i < N; i++, j--) { + pindexNext = pindexFirst; + // we measure our solve time in passing of blocks, where one bock == VERUS_BLOCK_POSUNITS units + for (int x = 0; x < params.nPOSAveragingWindow; x++) + { + solvetime += VERUS_BLOCK_POSUNITS; + pindexFirst = pindexFirst->pprev; + // in this loop, unqualified blocks are assumed POS + if (!pindexFirst || !DoesHashQualify(pindexFirst)) + break; + } + if (!pindexFirst) + break; + + // weighted sum + t += solvetime * j; + + // Target sum divided by a factor, (k N^2). + // The factor is a part of the final equation. However we divide + // here to avoid potential overflow. + bnTmp.SetCompact(pindexNext->nBits); // TODO(miketout): this must be POS nBits + sumTarget += bnTmp / (k * N * N); + } + + // Check we have enough blocks + if (!pindexFirst) + return nProofOfStakeLimit; + + // Keep t reasonable in case strange solvetimes occurred. + if (t < N * k / 3) + t = N * k / 3; + + bnTmp = bnLimit; + nextTarget = t * sumTarget; + if (nextTarget > bnTmp) + nextTarget = bnTmp; + + return nextTarget.GetCompact(); +} + bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& params) { if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH) @@ -184,7 +252,6 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); 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(); -CBlockIndex *komodo_chainactive(int32_t height); void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); extern int32_t KOMODO_CHOSEN_ONE; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; @@ -195,9 +262,10 @@ int32_t KOMODO_LOADINGBLOCKS = 1; extern std::string NOTARY_PUBKEY; -bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash,unsigned int nBits,const Consensus::Params& params,uint32_t blocktime) +bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t height, const Consensus::Params& params) { extern int32_t KOMODO_REWIND; + uint256 hash; bool fNegative,fOverflow; uint8_t origpubkey33[33]; int32_t i,nonzpkeys=0,nonz=0,special=0,special2=0,notaryid=-1,flag = 0, mids[66]; uint32_t tiptime,blocktimes[66]; arith_uint256 bnTarget; uint8_t pubkeys[66][33]; //for (i=31; i>=0; i--) @@ -206,7 +274,7 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash,unsigned int memcpy(origpubkey33,pubkey33,33); memset(blocktimes,0,sizeof(blocktimes)); tiptime = komodo_chainactive_timestamp(); - bnTarget.SetCompact(nBits, &fNegative, &fOverflow); + bnTarget.SetCompact(blkHeader.nBits, &fNegative, &fOverflow); if ( height == 0 ) { height = komodo_currentheight() + 1; @@ -226,7 +294,7 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash,unsigned int return(true); // will come back via different path with pubkey set } flag = komodo_eligiblenotary(pubkeys,mids,blocktimes,&nonzpkeys,height); - special2 = komodo_is_special(pubkeys,mids,blocktimes,height,pubkey33,blocktime); + special2 = komodo_is_special(pubkeys,mids,blocktimes,height,pubkey33,blkHeader.nTime); if ( notaryid >= 0 ) { if ( height > 10000 && height < 80000 && (special != 0 || special2 > 0) ) @@ -253,11 +321,13 @@ bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash,unsigned int arith_uint256 bnLimit = (height <= 1 || ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH) ? UintToArith256(params.powLimit) : UintToArith256(params.powAlternate); if (fNegative || bnTarget == 0 || fOverflow || bnTarget > bnLimit) return error("CheckProofOfWork(): nBits below minimum work"); + // Check proof of work matches claimed amount - if ( UintToArith256(hash) > bnTarget ) + if ( UintToArith256(hash = blkHeader.GetHash()) > bnTarget && !blkHeader.isVerusPOSBlock() ) { if ( KOMODO_LOADINGBLOCKS != 0 ) return true; + if ( ASSETCHAINS_SYMBOL[0] != 0 || height > 792000 ) { //if ( 0 && height > 792000 ) @@ -294,6 +364,9 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) bool fNegative; bool fOverflow; bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow); + + // TODO(miketout): proof of stake blocks must be marked as having the minimum POW in this context + if (fNegative || fOverflow || bnTarget == 0) return 0; // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 diff --git a/src/pow.h b/src/pow.h index 213fe228d..8221e8336 100644 --- a/src/pow.h +++ b/src/pow.h @@ -21,11 +21,13 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, int64_t nLastBlockTime, int64_t nFirstBlockTime, const Consensus::Params&); +unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); + /** Check whether the Equihash solution in a block header is valid */ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&); /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */ -bool CheckProofOfWork(int32_t height,uint8_t *pubkey33,uint256 hash, unsigned int nBits, const Consensus::Params&,uint32_t blocktime); +bool CheckProofOfWork(const CBlockHeader &blkHeader, uint8_t *pubkey33, int32_t height, const Consensus::Params& params); arith_uint256 GetBlockProof(const CBlockIndex& block); /** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */ diff --git a/src/primitives/block.h b/src/primitives/block.h index 7b5a06fe4..8dbcd7d5d 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -9,6 +9,7 @@ #include "primitives/transaction.h" #include "serialize.h" #include "uint256.h" +#include "arith_uint256.h" /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work @@ -88,6 +89,42 @@ public: { return (int64_t)nTime; } + + int32_t GetVerusPOSTarget() const + { + uint32_t nBits = 0; + + for (const unsigned char *p = nNonce.begin() + 3; p >= nNonce.begin(); p--) + { + nBits <<= 8; + nBits += *p; + } + return nBits; + } + + bool isVerusPOSBlock() const + { + arith_uint256 arNonce = UintToArith256(nNonce); + arith_uint256 tmpNonce = ((arNonce << 128) >> 128); + CVerusHashWriter hashWriter = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + hashWriter << ArithToUint256(tmpNonce); + return (nNonce == ArithToUint256(UintToArith256(hashWriter.GetHash()) << 128 | tmpNonce)); + } + + void SetVerusPOSTarget(int32_t nBits) + { + CVerusHashWriter hashWriter = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + uint256 hash; + arith_uint256 tmpNonce; + + arith_uint256 arNonce = UintToArith256(nNonce); + arNonce = ((arNonce >> 32) << 32) | nBits; + + tmpNonce = ((arNonce << 128) >> 128); + hashWriter << ArithToUint256(tmpNonce); + + nNonce = ArithToUint256(UintToArith256(hashWriter.GetHash()) << 128 | tmpNonce); + } }; @@ -108,7 +145,6 @@ public: CBlock(const CBlockHeader &header) { SetNull(); - *((CBlockHeader*)this) = header; } ADD_SERIALIZE_METHODS; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 112906061..d6d7051cf 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,7 +11,9 @@ #include "script/script.h" #include "serialize.h" #include "uint256.h" +#include "arith_uint256.h" #include "consensus/consensus.h" +#include "hash.h" #ifndef __APPLE__ #include @@ -24,6 +26,8 @@ #include "zcash/JoinSplit.hpp" #include "zcash/Proof.hpp" +extern uint32_t ASSETCHAINS_MAGIC; + class JSDescription { public: @@ -469,6 +473,28 @@ public: return a.hash != b.hash; } + // verus hash will be the same for a given txid, output number, block height, and blockhash of 100 blocks past + static uint256 _GetVerusPOSHash(const uint256 &txid, int32_t voutNum, int32_t height, const uint256 &pastHash, int64_t value) + { + CVerusHashWriter hashWriter = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + + hashWriter << ASSETCHAINS_MAGIC; + hashWriter << pastHash; + hashWriter << height; + hashWriter << txid; + hashWriter << voutNum; + return hashWriter.GetHash(); + } + + uint256 GetVerusPOSHash(int32_t voutNum, int32_t height, const uint256 &pastHash) const + { + uint256 txid = GetHash(); + if (voutNum >= vout.size()) + return uint256S("ff0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); + + return ArithToUint256(UintToArith256(_GetVerusPOSHash(txid, voutNum, height, pastHash, (uint64_t)vout[voutNum].nValue)) / vout[voutNum].nValue); + } + std::string ToString() const; }; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index a419b614e..c9bbd967d 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -259,7 +259,7 @@ UniValue generate(const UniValue& params, bool fHelp) LOCK(cs_main); pblock->nSolution = soln; solutionTargetChecks.increment(); - return CheckProofOfWork(chainActive.Height(),NOTARY_PUBKEY33,pblock->GetHash(), pblock->nBits, Params().GetConsensus(),pblock->nTime); + return CheckProofOfWork(*pblock,NOTARY_PUBKEY33,chainActive.Height(),Params().GetConsensus()); }; bool found = EhBasicSolveUncancellable(n, k, curr_state, validBlock); ehSolverRuns.increment(); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 7155c69e7..bbc87871b 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -58,6 +58,7 @@ extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAINS_CC; extern uint32_t ASSETCHAINS_MAGIC; extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY,ASSETCHAINS_LASTERA; +extern int32_t ASSETCHAINS_LWMAPOS; extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[]; UniValue getinfo(const UniValue& params, bool fHelp) @@ -202,6 +203,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("commission", ASSETCHAINS_COMMISSION)); if ( ASSETCHAINS_STAKED != 0 ) obj.push_back(Pair("staked", ASSETCHAINS_STAKED)); + if ( ASSETCHAINS_LWMAPOS != 0 ) + obj.push_back(Pair("verus proof of stake percent", ASSETCHAINS_LWMAPOS)); } return obj; } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 4eab398bb..207640504 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -222,10 +222,11 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) return whichType != TX_NONSTANDARD; } -bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) +bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet) { vector vSolutions; txnouttype whichType; + CScript scriptPubKey = _scriptPubKey; // if this is a CLTV script, get the destination after CLTV if (scriptPubKey.IsCheckLockTimeVerify()) @@ -234,10 +235,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) uint32_t scriptStart = pushOp + 3; // check post CLTV script - CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - - // check again with only postfix subscript - return(ExtractDestination(postfix, addressRet)); + scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); } if (!Solver(scriptPubKey, whichType, vSolutions)) diff --git a/src/txdb.cpp b/src/txdb.cpp index c856947ad..0651b920c 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -532,7 +532,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts() { uint8_t pubkey33[33]; komodo_index2pubkey33(pubkey33,pindexNew,pindexNew->nHeight); - if (!CheckProofOfWork(pindexNew->nHeight,pubkey33,pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus(),pindexNew->nTime)) + if (!CheckProofOfWork(header,pubkey33,pindexNew->nHeight,Params().GetConsensus())) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); } pcursor->Next(); diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp index 3d3327139..6889d4cc1 100644 --- a/src/wallet-utility.cpp +++ b/src/wallet-utility.cpp @@ -19,6 +19,8 @@ uint32_t ASSETCHAINS_MAGIC = 2387029918; uint32_t ASSETCHAINS_EQUIHASH = 0; uint32_t ASSETCHAINS_VERUSHASH = 1; uint32_t ASSETCHAINS_ALGO = 0; +int32_t ASSETCHAINS_LWMAPOS = 0; +int32_t VERUS_BLOCK_POSUNITS = 1000; unsigned int MAX_BLOCK_SIGOPS = 20000; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f66a1e381..49ea9270d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4660,3 +4660,8 @@ int32_t komodo_staked(CPubKey &pubkey, CMutableTransaction &txNew,uint32_t nBits } return(siglen); } + +int32_t verus_staked(CPubKey &pubkey, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig) +{ + return pwalletMain->VerusStakeTransaction(txNew, nBits, hashResult, utxosig); +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0637d3fd4..c3451afc0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -21,6 +21,7 @@ #include "zcash/Note.hpp" #include "crypter.h" #include "coins.h" + #include #include @@ -43,6 +44,8 @@ bool fPayAtLeastCustomFee = true; extern int32_t KOMODO_EXCHANGEWALLET; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern int32_t VERUS_MIN_STAKEAGE; +CBlockIndex *komodo_chainactive(int32_t height); /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -991,6 +994,106 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, return txOrdered; } +// looks through all wallet UTXOs and checks to see if any qualify to stake the block at the current height. it always returns the qualified +// UTXO with the smallest coin age if there is more than one, as larger coin age will win more often and is worth saving +// each attempt consists of taking a VerusHash of the following values: +// ASSETCHAINS_MAGIC, nHeight, txid, voutNum +bool CWallet::VerusSelectStakeOutput(arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, const arith_uint256 &target) const +{ + arith_uint256 curHash; + vector vecOutputs; + COutput *pwinner = NULL; + CBlockIndex *pastBlockIndex; + txnouttype whichType; + std:vector> vSolutions; + + pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); + + if (pastBlockIndex = komodo_chainactive(nHeight - COINBASE_MATURITY)) + { + uint256 pastHash = pastBlockIndex->GetBlockHash(); + + BOOST_FOREACH(COutput &txout, vecOutputs) + { + if (txout.fSpendable && (UintToArith256(txout.tx->GetVerusPOSHash(txout.i, nHeight, pastHash)) <= target) && (txout.nDepth >= VERUS_MIN_STAKEAGE)) + { + // get the smallest winner + if (Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH) && + (!pwinner || pwinner->tx->vout[pwinner->i].nValue > txout.tx->vout[txout.i].nValue)) + pwinner = &txout; + } + } + if (pwinner) + { + stakeSource = *(pwinner->tx); + voutNum = pwinner->i; + return true; + } + } + return false; +} + +int32_t CWallet::VerusStakeTransaction(CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig) const +{ + arith_uint256 target; + CTransaction stakeSource; + int32_t voutNum, siglen = 0; + int64_t nValue; + txnouttype whichType; + std::vector> vSolutions; + + CBlockIndex *tipindex = chainActive.Tip(); + bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus()); + target.SetCompact(bnTarget); + + if (!VerusSelectStakeOutput(hashResult, stakeSource, voutNum, tipindex->nHeight + 1, target) || + !Solver(stakeSource.vout[voutNum].scriptPubKey, whichType, vSolutions)) + { + return 0; + } + + // komodo create transaction code below this line + bool signSuccess; + SignatureData sigdata; + uint64_t txfee; + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + + const CKeyStore& keystore = *pwalletMain; + txNew.vin.resize(1); + txNew.vout.resize(1); + txfee = 0; + txNew.vin[0].prevout.hash = stakeSource.GetHash(); + txNew.vin[0].prevout.n = voutNum; + + if (whichType == TX_PUBKEY) + { + txNew.vout[0].scriptPubKey << ToByteVector(vSolutions[0]) << OP_CHECKSIG; + } + else if (whichType == TX_PUBKEYHASH) + { + txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG; + } + else + return 0; + + nValue = txNew.vout[0].nValue = voutNum - txfee; + txNew.nLockTime = 0; + CTransaction txNewConst(txNew); + signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, sigdata, consensusBranchId); + if (!signSuccess) + fprintf(stderr,"failed to create signature\n"); + else + { + uint8_t *ptr; + UpdateTransaction(txNew,0,sigdata); + ptr = (uint8_t *)sigdata.scriptSig.data(); + siglen = sigdata.scriptSig.size(); + for (int i=0; i