Browse Source

Verus Proof of Stake Compete with Additional ant-fork protection on block 1

pull/4/head
Michael Toutonghi 6 years ago
parent
commit
1f722359c1
  1. 21
      src/chainparams.cpp
  2. 12
      src/consensus/params.h
  3. 4
      src/crypto/haraka.h
  4. 121
      src/komodo_bitcoind.h
  5. 5
      src/komodo_globals.h
  6. 17
      src/komodo_utils.h
  7. 31
      src/main.cpp
  8. 26
      src/miner.cpp
  9. 83
      src/pow.cpp
  10. 4
      src/pow.h
  11. 37
      src/primitives/block.h
  12. 25
      src/primitives/transaction.h
  13. 2
      src/rpcmining.cpp
  14. 3
      src/rpcmisc.cpp
  15. 8
      src/script/standard.cpp
  16. 2
      src/txdb.cpp
  17. 2
      src/wallet-utility.cpp
  18. 5
      src/wallet/rpcwallet.cpp
  19. 100
      src/wallet/wallet.cpp
  20. 3
      src/wallet/wallet.h

21
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 = 100;
// 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 = 100446;
}
checkpointData = //(Checkpoints::CCheckpointData)
{
boost::assign::map_list_of

12
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; }

4
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];

121
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<vector<unsigned char>> vch = vector<vector<unsigned char>>();
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,95 @@ 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;
arith_uint256 target, hash;
CTransaction tx;
// TODO(miketout) must initialize destaddr properly
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())) / tx.vout[voutNum].nValue;
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) )
{
strcpy(voutaddr, CBitcoinAddress(voutaddress).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 +1369,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;

5
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 = 200; // 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;

17
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);
}

31
src/main.cpp

@ -907,6 +907,14 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
*/
bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight)
{
int64_t valOut = tx.GetValueOut(), sub = komodo_ac_block_subsidy(nHeight);
if ((uint64_t)(tx.GetValueOut()) != komodo_ac_block_subsidy(nHeight))
{
// failed with invalid coinbase output
fprintf(stderr, "ERROR: invalid block #%i, reward=%li, subsidy should be %li", nHeight, valOut, sub);
return(false);
}
// if time locks are on, ensure that this coin base is time locked exactly as it should be
if ((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE)
{
@ -1731,7 +1739,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 +1782,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
@ -3328,6 +3333,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 +3859,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 +3945,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 +4009,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 +4018,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()

26
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 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,24 @@ 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();
}
else
{
siglen = komodo_staked(key, txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig);
}
if ( siglen > 0 )
{
CAmount txfees = 0;
if ( GetAdjustedTime() < blocktime-13 )
@ -1478,7 +1492,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<bool(std::vector<unsigned char>)> validBlock =
@ -1726,13 +1740,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));
}

83
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

4
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. */

37
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,41 @@ 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 += *p;
nBits <<= 8;
}
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 +144,6 @@ public:
CBlock(const CBlockHeader &header)
{
SetNull();
*((CBlockHeader*)this) = header;
}
ADD_SERIALIZE_METHODS;

25
src/primitives/transaction.h

@ -12,6 +12,7 @@
#include "serialize.h"
#include "uint256.h"
#include "consensus/consensus.h"
#include "hash.h"
#ifndef __APPLE__
#include <stdint.h>
@ -24,6 +25,8 @@
#include "zcash/JoinSplit.hpp"
#include "zcash/Proof.hpp"
extern uint32_t ASSETCHAINS_MAGIC;
class JSDescription
{
public:
@ -469,6 +472,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 uint256();
return _GetVerusPOSHash(txid, voutNum, height, pastHash, (uint64_t)vout[voutNum].nValue);
}
std::string ToString() const;
};

2
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();

3
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;
}

8
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<valtype> 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))

2
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();

2
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;

5
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(pubkey, txNew, nBits, hashResult, utxosig);
}

100
src/wallet/wallet.cpp

@ -43,6 +43,7 @@ bool fPayAtLeastCustomFee = true;
extern int32_t KOMODO_EXCHANGEWALLET;
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
CBlockIndex *komodo_chainactive(int32_t height);
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
@ -991,6 +992,105 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& 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<COutput> vecOutputs;
COutput *pwinner = NULL;
CBlockIndex *pastBlockIndex;
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false);
if (pastBlockIndex = komodo_chainactive(nHeight - COINBASE_MATURITY))
{
uint256 pastHash = pastBlockIndex->GetBlockHash();
BOOST_FOREACH(COutput &txout, vecOutputs)
{
if ((curHash = UintToArith256(txout.tx->GetVerusPOSHash(txout.i, nHeight, pastHash)) / txout.tx->vout[txout.i].nValue) <= target &&
txout.fSpendable)
{
// get the smallest winner
if (!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(CPubKey &pubkey, 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;
CBlockIndex *tipindex = chainActive.Tip();
bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus());
target.SetCompact(bnTarget);
if (!VerusSelectStakeOutput(hashResult, stakeSource, voutNum, tipindex->nHeight, target))
{
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;
/*
uint8_t *script;
int32_t i;
uint8_t *ptr;
txNew.vout[0].scriptPubKey.resize(35);
ptr = (uint8_t *)pubkey.begin();
script = (uint8_t *)(txNew.vout[0].scriptPubKey.data());
script[0] = 33;
for (i=0; i<33; i++)
script[i+1] = ptr[i];
script[34] = OP_CHECKSIG;
*/
txNew.vout[0].scriptPubKey << ToByteVector(pubkey) << OP_CHECKSIG;
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<siglen; i++)
utxosig[i] = ptr[i];//, fprintf(stderr,"%02x",ptr[i]);
}
return(siglen);
}
void CWallet::MarkDirty()
{
{

3
src/wallet/wallet.h

@ -1148,6 +1148,9 @@ public:
bool ignoreSpent=true,
bool ignoreUnspendable=true);
// staking functions
bool VerusSelectStakeOutput(arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, const arith_uint256 &target) const;
int32_t VerusStakeTransaction(CPubKey &pubkey, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig) const;
};
/** A key allocated from the key pool. */

Loading…
Cancel
Save