Browse Source

Merge branch 'dev' of github.com:miketout/VerusCoin into dev

# Conflicts:
#	src/deprecation.h
pull/226/head
Asher 5 years ago
parent
commit
f2a1b991f0
  1. 8
      src/cc/CCcustom.cpp
  2. 2
      src/cc/CCinclude.h
  3. 8
      src/cc/eval.cpp
  4. 9
      src/chainparams.cpp
  5. 2
      src/cheatcatcher.cpp
  6. 15
      src/coins.cpp
  7. 2
      src/deprecation.h
  8. 5
      src/komodo.h
  9. 2
      src/komodo_bitcoind.h
  10. 2
      src/komodo_globals.h
  11. 26
      src/komodo_utils.h
  12. 361
      src/main.cpp
  13. 9
      src/main.h
  14. 388
      src/miner.cpp
  15. 2
      src/miner.h
  16. 2
      src/mmr.h
  17. 6
      src/net.cpp
  18. 4
      src/pbaas/crosschainrpc.cpp
  19. 870
      src/pbaas/notarization.cpp
  20. 44
      src/pbaas/notarization.h
  21. 291
      src/pbaas/pbaas.cpp
  22. 185
      src/pbaas/pbaas.h
  23. 13
      src/pow.cpp
  24. 6
      src/primitives/block.cpp
  25. 42
      src/primitives/block.h
  26. 4
      src/primitives/solutiondata.h
  27. 6
      src/primitives/transaction.cpp
  28. 5
      src/rpc/mining.cpp
  29. 4
      src/rpc/misc.cpp
  30. 785
      src/rpc/pbaasrpc.cpp
  31. 2
      src/rpc/pbaasrpc.h
  32. 2
      src/script/script.cpp
  33. 8
      src/script/script_ext.cpp
  34. 66
      src/txmempool.cpp
  35. 1
      src/wallet/asyncrpcoperation_sendmany.cpp
  36. 6
      src/wallet/asyncrpcoperation_shieldcoinbase.cpp
  37. 25
      src/wallet/wallet.cpp

8
src/cc/CCcustom.cpp

@ -322,6 +322,14 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
break;
case EVAL_SERVICEREWARD:
strcpy(cp->unspendableCCaddr,ServiceRewardAddr.c_str());
strcpy(cp->normaladdr,ServiceRewardAddr.c_str());
strcpy(cp->CChexstr,ServiceRewardPubKey.c_str());
memcpy(cp->CCpriv,DecodeSecret(ServiceRewardWIF).begin(),32);
cp->validate = ValidateServiceReward;
cp->ismyvin = IsServiceRewardInput;
break;
case EVAL_RESERVEIMPORT:
case EVAL_RESERVEEXPORT:
case EVAL_RESERVEOUTPUT:

2
src/cc/CCinclude.h

@ -99,7 +99,7 @@ bool GetAddressUnspent(uint160 addressHash, int type,std::vector<std::pair<CAddr
static const uint256 zeroid;
bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock);
int32_t is_hexstr(char *str,int32_t n);
bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL);
bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL, int32_t simHeight = 0, bool *missinginputs = NULL);
//uint64_t myGettxout(uint256 hash,int32_t n);
bool myIsutxo_spentinmempool(uint256 txid,int32_t vout);
int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout);

8
src/cc/eval.cpp

@ -74,6 +74,14 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
switch ( ecode )
{
case EVAL_STAKEGUARD:
case EVAL_PBAASDEFINITION:
case EVAL_SERVICEREWARD:
case EVAL_EARNEDNOTARIZATION:
case EVAL_ACCEPTEDNOTARIZATION:
case EVAL_FINALIZENOTARIZATION:
case EVAL_RESERVEOUTPUT:
case EVAL_RESERVEEXPORT:
case EVAL_RESERVEIMPORT:
return(ProcessCC(cp,this, vparams, txTo, nIn));
break;

9
src/chainparams.cpp

@ -235,7 +235,7 @@ void *chainparams_commandline(void *ptr)
mainParams.pchMessageStart[1] = (ASSETCHAINS_MAGIC >> 8) & 0xff;
mainParams.pchMessageStart[2] = (ASSETCHAINS_MAGIC >> 16) & 0xff;
mainParams.pchMessageStart[3] = (ASSETCHAINS_MAGIC >> 24) & 0xff;
fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %u coins\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %lu coins\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,ASSETCHAINS_SUPPLY / COIN);
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
{
@ -678,7 +678,12 @@ static CRegTestParams regTestParams;
static CChainParams *pCurrentParams = 0;
const CChainParams &Params() {
assert(pCurrentParams);
// the only reason this should occur is before initialization to convert addresses
// we don't use others besides main, and the only risk in returning it would be something else not initialized
if (!pCurrentParams)
{
return mainParams;
}
return *pCurrentParams;
}

2
src/cheatcatcher.cpp

@ -30,7 +30,7 @@ uint32_t CCheatList::Prune(uint32_t height)
if (height > 0 && NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING))
{
LOCK(cs_cheat);
for (auto it = orderedCheatCandidates.begin(); it != orderedCheatCandidates.end() && it->second.height <= height; it--)
for (auto it = orderedCheatCandidates.begin(); it != orderedCheatCandidates.end() && it->second.height <= height; it++)
{
toPrune.push_back(&it->second);
}

15
src/coins.cpp

@ -590,7 +590,17 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
return 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
value = GetOutputFor(tx.vin[i]).nValue;
value = 0;
const CCoins* coins = AccessCoins(tx.vin[i].prevout.hash);
if (coins && coins->IsAvailable(tx.vin[i].prevout.n))
{
value = coins->vout[tx.vin[i].prevout.n].nValue;
}
else
{
return 0;
}
nResult += value;
#ifdef KOMODO_ENABLE_INTEREST
if ( ASSETCHAINS_SYMBOL[0] == 0 && nHeight >= 60000 )
@ -660,8 +670,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
{
if (!tx.IsMint()) {
int checkUntil = IsBlockBoundTransaction(tx) ? tx.vin.size() - 1 : tx.vin.size();
for (unsigned int i = 0; i < checkUntil; i++) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const CCoins* coins = AccessCoins(prevout.hash);
if (!coins || !coins->IsAvailable(prevout.n)) {

2
src/deprecation.h

@ -8,7 +8,7 @@
// Deprecation policy:
// * Shut down 16 weeks' worth of blocks after the estimated release block height.
// * A warning is shown during the 2 weeks' worth of blocks prior to shut down.
static const int APPROX_RELEASE_HEIGHT = 499061;
static const int APPROX_RELEASE_HEIGHT = 500000;
static const int WEEKS_UNTIL_DEPRECATION = 20;
static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 60 * 24);

5
src/komodo.h

@ -853,10 +853,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block)
}
} else
{
if (!IsBlockBoundTransaction(block.vtx[i]))
{
printf("cant get scriptPubKey for ht.%d txi.%d vin.%d\n",height,i,j);
}
printf("cant get scriptPubKey for ht.%d txi.%d vin.%d\n",height,i,j);
}
}
numvalid = bitweight(signedmask);

2
src/komodo_bitcoind.h

@ -1515,7 +1515,7 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
for (int i = 0; validHash && i < pblock->vtx[0].vout.size(); i++)
{
validHash = false;
if (ValidateMatchingStake(pblock->vtx[0], i, pblock->vtx[txn_count-1], validHash) && !validHash)
if (pblock->vtx[0].vout[i].scriptPubKey.IsInstantSpend() || ValidateMatchingStake(pblock->vtx[0], i, pblock->vtx[txn_count-1], validHash) && !validHash)
{
if ((p.prevHash == pblock->hashPrevBlock) && (int32_t)p.blkHeight == height)
{

2
src/komodo_globals.h

@ -144,7 +144,7 @@ int64_t komodo_current_supply(uint32_t nHeight)
else
{
// figure out max_money by adding up supply to a maximum of 10,000,000 blocks
cur_money = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff) + ASSETCHAINS_GENESISTXVAL;
cur_money = (ASSETCHAINS_SUPPLY+1) + (ASSETCHAINS_MAGIC & 0xffffff) + ASSETCHAINS_GENESISTXVAL;
if ( ASSETCHAINS_LASTERA == 0 && ASSETCHAINS_REWARD[0] == 0 )
{
cur_money += (nHeight * 10000) / SATOSHIDEN;

26
src/komodo_utils.h

@ -1674,11 +1674,9 @@ 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);
{
subsidy += ASSETCHAINS_SUPPLY + (ASSETCHAINS_MAGIC & 0xffffff);
}
return(subsidy);
}
@ -1789,7 +1787,7 @@ void komodo_args(char *argv0)
mapArgs["-ac_cc"] = "1";
mapArgs["-ac_supply"] = "0";
mapArgs["-ac_eras"] = "3";
mapArgs["-ac_reward"] = "38400000000,38400000000,38400000000";
mapArgs["-ac_reward"] = "50000000000,38400000000,38400000000";
mapArgs["-ac_halving"] = "1,43200,1051920";
mapArgs["-ac_decay"] = "100000000,0,0";
mapArgs["-ac_end"] = "10080,226080,0";
@ -1837,6 +1835,7 @@ void komodo_args(char *argv0)
{
throw error("Cannot find chain data");
}
name = string(ASSETCHAINS_SYMBOL);
paramsLoaded = true;
}
catch(const std::exception& e)
@ -1886,8 +1885,9 @@ void komodo_args(char *argv0)
printf("KOMODO_REWIND %d\n",KOMODO_REWIND);
}
if ( name.c_str()[0] != 0 )
if ( name.size() )
{
if (!paramsLoaded)
{
std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[1]));
@ -2034,7 +2034,8 @@ void komodo_args(char *argv0)
if ( strlen(addn.c_str()) > 0 )
ASSETCHAINS_SEED = 1;
strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1);
memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
strcpy(ASSETCHAINS_SYMBOL, name.c_str());
ASSETCHAINS_CHAINID = CCrossChainRPCData::GetChainID(std::string(ASSETCHAINS_SYMBOL));
@ -2058,8 +2059,15 @@ void komodo_args(char *argv0)
int32_t komodo_baseid(char *origbase);
extern int COINBASE_MATURITY;
if ( (port= komodo_userpass(ASSETCHAINS_USERPASS, ASSETCHAINS_SYMBOL)) != 0 )
{
ASSETCHAINS_RPCPORT = port;
else komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1);
}
else
{
komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1);
komodo_userpass(ASSETCHAINS_USERPASS, ASSETCHAINS_SYMBOL); // make sure we set user and password on first load
}
if (ASSETCHAINS_LASTERA == 0 && ASSETCHAINS_REWARD[0] == 0)
COINBASE_MATURITY = 1;
//fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_RPCPORT);

361
src/main.cpp

@ -731,22 +731,27 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight)
}
}
BOOST_FOREACH(const CTxIn& txin, tx.vin)
bool isCoinbase = tx.IsCoinBase();
if (!isCoinbase)
{
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys. (remember the 520 byte limit on redeemScript size) That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard)
if (txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys. (remember the 520 byte limit on redeemScript size) That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20
// CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
// considered standard)
if (txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
}
}
}
@ -774,7 +779,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight)
else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
} else if (txout.scriptPubKey.IsPayToCryptoCondition() == 0 && txout.IsDust(::minRelayTxFee)) {
} else if (txout.scriptPubKey.IsPayToCryptoCondition() == 0 && !isCoinbase && txout.IsDust(::minRelayTxFee)) {
reason = "dust";
return false;
}
@ -960,24 +965,28 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
*/
bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight)
{
// if time locks are on, ensure that this coin base is time locked exactly as it should be
bool valid = true, timelocked = false;
CTxDestination firstDest;
// if time locks are on, ensure that this coin base is time locked exactly as it should be, or invalidate
if (((uint64_t)tx.GetValueOut() >= ASSETCHAINS_TIMELOCKGTE) ||
(((nHeight >= 31680) || strcmp(ASSETCHAINS_SYMBOL, "VRSC") != 0) && komodo_ac_block_subsidy(nHeight) >= ASSETCHAINS_TIMELOCKGTE))
{
CScriptID scriptHash;
valid = false;
timelocked = true;
// to be valid, it must be a P2SH transaction and have an op_return in vout[1] that
// holds the full output script, which may include multisig, etc., but starts with
// the time lock verify of the correct time lock for this block height
if (tx.vout.size() == 2 &&
CScriptExt(tx.vout[0].scriptPubKey).IsPayToScriptHash(&scriptHash) &&
tx.vout[1].scriptPubKey.size() >= 7 && // minimum for any possible future to prevent out of bounds
tx.vout[1].scriptPubKey[0] == OP_RETURN)
if (CScriptExt(tx.vout[0].scriptPubKey).IsPayToScriptHash(&scriptHash) &&
tx.vout.back().scriptPubKey.size() >= 7 && // minimum for any possible future to prevent out of bounds
tx.vout.back().scriptPubKey[0] == OP_RETURN)
{
opcodetype op;
std::vector<uint8_t> opretData = std::vector<uint8_t>();
CScript::const_iterator it = tx.vout[1].scriptPubKey.begin() + 1;
if (tx.vout[1].scriptPubKey.GetOp2(it, op, &opretData))
CScript::const_iterator it = tx.vout.back().scriptPubKey.begin() + 1;
if (tx.vout.back().scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData.data()[0] == OPRETTYPE_TIMELOCK)
{
@ -988,14 +997,42 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh
opretScript.IsCheckLockTimeVerify(&unlocktime) &&
komodo_block_unlocktime(nHeight) == unlocktime)
{
return(true);
if (ExtractDestination(opretScript, firstDest))
{
valid = true;
}
}
}
}
}
return(false);
}
return(true);
// if there is a premine, make sure it is the right amount and goes to the correct recipient
if (!IsVerusActive && valid && nHeight == 1)
{
if (ConnectedChains.ThisChain().premine && !ConnectedChains.ThisChain().address.IsNull())
{
if (!timelocked && !(ExtractDestination(tx.vout[0].scriptPubKey, firstDest)))
{
valid = false;
}
else if (tx.vout[0].nValue != ConnectedChains.ThisChain().premine || GetDestinationID(firstDest) != ConnectedChains.ThisChain().address)
{
valid = false;
}
}
// ensure that if this is a PBaaS chain, block 1 includes notarization appropriately derived from the chain definition
// transaction. the coinbase must contain a matching notarization out, and the notarization must agree with our start height
CPBaaSNotarization pbn(tx);
if (!pbn.IsValid() || pbn.notarizationHeight < ConnectedChains.ThisChain().startBlock)
{
valid = false;
}
}
return valid;
}
/**
@ -1511,14 +1548,24 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
return nMinFee;
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel)
{
if (tx.IsCoinBase())
{
fprintf(stderr,"Cannot accept coinbase as individual tx\n");
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),REJECT_INVALID, "coinbase");
}
return AcceptToMemoryPoolInt(pool, state, tx, fLimitFree, pfMissingInputs, fRejectAbsurdFee, dosLevel);
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel)
bool AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee, int dosLevel, int32_t simHeight)
{
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
int flag=0,nextBlockHeight = chainActive.Height() + 1;
int flag=0, nextBlockHeight = simHeight ? simHeight : chainActive.Height() + 1;
auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus());
// Node operator can choose to reject tx by number of transparent inputs
@ -1531,7 +1578,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
size_t n = tx.vin.size();
if (n > limit) {
LogPrint("mempool", "Dropping txid %s : too many transparent inputs %zu > limit %zu\n", tx.GetHash().ToString(), n, limit );
return false;
return error("AcceptToMemoryPool: too many transparent inputs");
}
}
@ -1551,12 +1598,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
{
return error("AcceptToMemoryPool: ContextualCheckTransaction failed");
}
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
{
fprintf(stderr,"AcceptToMemoryPool coinbase as individual tx\n");
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),REJECT_INVALID, "coinbase");
}
// Coinbase is only valid in a block, not as a loose transaction. we will put it in the mem pool to enable
// instant spend features, but we will not relay coinbase transactions
//if (tx.IsCoinBase())
//{
// fprintf(stderr,"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 (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight))
@ -1565,6 +1615,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
//fprintf(stderr,"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.
@ -1581,8 +1632,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
//fprintf(stderr,"already in mempool\n");
return state.Invalid(false, REJECT_DUPLICATE, "already in mempool");
}
bool iscoinbase = tx.IsCoinBase();
// Check for conflicts with in-memory transactions
// TODO: including conflicts in chain definition and notarizations
if(!iscoinbase)
{
LOCK(pool.cs); // protect pool.mapNextTx
for (unsigned int i = 0; i < tx.vin.size(); i++)
@ -1591,7 +1646,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if (pool.mapNextTx.count(outpoint))
{
// Disable replacement feature for now
return false;
//fprintf(stderr,"pool.mapNextTx.count\n");
return error("inputs have been spent in mempool");
}
}
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
@ -1625,14 +1681,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
//fprintf(stderr,"view.HaveCoins(hash) error\n");
return state.Invalid(false, REJECT_DUPLICATE, "already have coins");
}
if (tx.IsCoinImport())
{
// Inverse of normal case; if input exists, it's been spent
if (ExistsImportTombstone(tx, view))
return state.Invalid(false, REJECT_DUPLICATE, "import tombstone exists");
}
else
else if (!iscoinbase)
{
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
@ -1666,7 +1722,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Bring the best block into scope
view.GetBestBlock();
nValueIn = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime);
// store coinbases as having the same value in as out
nValueIn = iscoinbase ? tx.GetValueOut() : view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime);
if ( 0 && interest != 0 )
fprintf(stderr,"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
@ -1704,7 +1761,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Keep track of transactions that spend a coinbase and are not "InstantSpend:", which we re-scan
// during reorgs to ensure COINBASE_MATURITY is still met.
bool fSpendsCoinbase = false;
if (!tx.IsCoinImport()) {
if (!iscoinbase && !tx.IsCoinImport()) {
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
if (coins->IsCoinBase() && !coins->vout[txin.prevout.n].scriptPubKey.IsInstantSpend()) {
@ -1726,9 +1783,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
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.
} else {
// Don't accept it if it can't get into a block
// Don't accept it if it can't get into a block, if it's a coinbase, it's here to be recognized, not to go somewhere else
CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
if (fLimitFree && nFees < txMinFee)
if (!iscoinbase && fLimitFree && nFees < txMinFee)
{
//fprintf(stderr,"accept failure.5\n");
return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee");
@ -1736,7 +1793,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
}
// 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))) {
if (!iscoinbase && GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
fprintf(stderr,"accept failure.6\n");
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}
@ -1744,7 +1801,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// 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.
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
if (!iscoinbase && fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
{
static CCriticalSection csFreeLimiter;
static double dFreeCount;
@ -1822,7 +1879,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
}
// Add memory spent index
if (fSpentIndex) {
if (!iscoinbase && fSpentIndex) {
pool.addSpentIndex(entry, view);
}
}
@ -1894,17 +1951,23 @@ bool GetAddressUnspent(uint160 addressHash, int type,
else return(coins.vout[n].nValue);
}*/
bool myAddtomempool(CTransaction &tx, CValidationState *pstate)
bool myAddtomempool(CTransaction &tx, CValidationState *pstate, int32_t simHeight, bool *missinginputs)
{
CValidationState state;
if (!pstate)
pstate = &state;
CTransaction Ltx; bool fMissingInputs,fOverrideFees = false;
CTransaction Ltx; bool fOverrideFees = false;
if ( mempool.lookup(tx.GetHash(),Ltx) == 0 )
return(AcceptToMemoryPool(mempool, *pstate, tx, false, &fMissingInputs, !fOverrideFees));
return(AcceptToMemoryPoolInt(mempool, *pstate, tx, false, missinginputs, !fOverrideFees, -1, simHeight));
else return(true);
}
void myRemovefrommempool(const CTransaction &tx)
{
std::list<CTransaction> removed;
mempool.remove(tx, removed, true);
}
bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock)
{
// need a GetTransaction without lock so the validation code for assets can run without deadlock
@ -2145,6 +2208,11 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
//return nSubsidy;
}
CAmount GetBlockOnePremine()
{
return ASSETCHAINS_SUPPLY;
}
bool IsInitialBlockDownload()
{
const CChainParams& chainParams = Params();
@ -2365,19 +2433,23 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
{
int checkUntil = IsBlockBoundTransaction(tx) ? tx.vin.size() - 1 : tx.vin.size();
if (!tx.IsMint()) // mark inputs spent
{
txundo.vprevout.reserve(checkUntil);
for (int i = 0; i < checkUntil; i++)
txundo.vprevout.reserve(tx.vin.size());
for (int i = 0; i < tx.vin.size(); i++)
{
const CTxIn &txin = tx.vin[i];
const CTxIn &txin = tx.vin[i];
CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash);
unsigned nPos = txin.prevout.n;
if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull())
{
printf("Failed to find coins for transaction %s, output %d, at height %d\n", txin.prevout.hash.GetHex().c_str(), txin.prevout.n, nHeight);
LogPrintf("Failed to find coins for transaction %s, output %d, at height %d\n", txin.prevout.hash.GetHex().c_str(), txin.prevout.n, nHeight);
// we can't generate undo information for this, allow if it's a block bound transaction
assert(false);
}
// mark an outpoint spent, and construct undo information
txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos]));
coins->Spend(nPos);
@ -2894,7 +2966,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
// restore inputs
if (!tx.IsMint()) {
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
if (txundo.vprevout.size() != tx.vin.size() && !(IsBlockBoundTransaction(tx) && (txundo.vprevout.size() + 1) == tx.vin.size()))
if (txundo.vprevout.size() != tx.vin.size())
return error("DisconnectBlock(): transaction and undo data inconsistent");
for (unsigned int j = txundo.vprevout.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
@ -3171,8 +3243,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
const CCoins* coins = view.AccessCoins(tx.GetHash());
if (coins && !coins->IsPruned())
{
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30");
}
}
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
@ -3241,12 +3315,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->GetHeight(),tx.nLockTime);
CPBaaSNotarization tpbn;
bool isBlockBoundSmartTx = (IsBlockBoundTransaction(tx) &&
block.vtx[0].vout.size() > tx.vin.back().prevout.n &&
block.vtx[0].vout[tx.vin.back().prevout.n].scriptPubKey.IsInstantSpend() &&
::GetHash(CPBaaSNotarization(block.vtx[0])) == tx.vin.back().prevout.hash);
(tpbn = CPBaaSNotarization(tx)).IsValid() && // these need to be generic tests vs. notarization
::GetHash(CPBaaSNotarization(block.vtx[0])) == ::GetHash(tpbn));
if (!tx.IsMint() && !isBlockBoundSmartTx)
if (!tx.IsMint())
{
if (!view.HaveInputs(tx))
{
@ -3772,7 +3848,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
list<CTransaction> removed;
CValidationState stateDummy;
// don't keep staking or invalid transactions
// don't keep coinbase, staking, or invalid transactions
if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL))
{
mempool.remove(tx, removed, true);
@ -3834,6 +3910,13 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
void RemoveCoinbaseFromMemPool(const CBlock& block)
{
// remove coinbase and anything that depended on it sooner, rather than later, if failure
LOCK2(cs_main, mempool.cs);
myRemovefrommempool(block.vtx[0]);
}
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
@ -3866,6 +3949,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
KOMODO_CONNECTING = -1;
GetMainSignals().BlockChecked(*pblock, state);
if (!rv) {
RemoveCoinbaseFromMemPool(*pblock);
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
@ -4537,7 +4621,7 @@ int32_t komodo_checkPOW(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,
bool fCheckPOW, bool fCheckMerkleRoot)
bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckTxInputs)
{
uint8_t pubkey33[33]; uint256 hash;
// These are checks that are independent of context.
@ -4604,48 +4688,57 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
// Check transactions
CTransaction sTx;
CTransaction *ptx = NULL;
bool success = true;
if ( ASSETCHAINS_CC != 0 ) // CC contracts might refer to transactions in the current block, from a CC spend within the same block and out of order
{
int32_t i,j,rejects=0,lastrejects=0;
//fprintf(stderr,"put block's tx into mempool\n");
// we need this lock to prevent accepting transactions we shouldn't
LOCK(mempool.cs);
//printf("checking block %d\n", height);
while ( 1 )
{
for (i=0; i<block.vtx.size(); i++)
for (i = block.hashPrevBlock.IsNull() ? 1 : 0; i < block.vtx.size(); i++)
{
CValidationState state;
CTransaction Tx;
const CTransaction &tx = (CTransaction)block.vtx[i];
if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block) != 0)))
if (((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block) != 0)))
continue;
Tx = tx;
if ( myAddtomempool(Tx, &state) == false ) // happens with out of order tx in block on resync
bool missinginputs = false;
if ( myAddtomempool(Tx, &state, height, &missinginputs) == false ) // happens with out of order tx in block on resync
{
//LogPrintf("Rejected by mempool, reason: .%s.\n", state.GetRejectReason().c_str());
uint32_t ecode;
// take advantage of other checks, but if we were only rejected because it is a valid staking
// take advantage of other checks, but if we were only rejected because it is present or a valid staking
// transaction, sync with wallets and don't mark as a reject
if (i == (block.vtx.size() - 1) && ASSETCHAINS_LWMAPOS && block.IsVerusPOSBlock() && state.GetRejectReason() == "staking")
{
sTx = Tx;
ptx = &sTx;
} else
} else if (state.GetRejectReason() != "already have coins" &&
!((missinginputs || state.GetRejectCode() == REJECT_DUPLICATE) && !fCheckTxInputs)) // fCheckPOW overloaded for DB verify to pass
{
if (!(state.GetRejectReason() == "bad-txns-inputs-missing" &&
IsBlockBoundTransaction(Tx) &&
block.vtx[0].vout.size() > Tx.vin.back().prevout.n &&
block.vtx[0].vout[Tx.vin.back().prevout.n].scriptPubKey.IsInstantSpend() &&
::GetHash(CPBaaSNotarization(block.vtx[0])) == Tx.vin.back().prevout.hash))
printf("Rejected transaction for %s, reject code %d\n", state.GetRejectReason().c_str(), state.GetRejectCode());
for (auto input : Tx.vin)
{
// unless this is bound to the coinbase as a notarization, reject it
rejects++;
LogPrintf("Notarization input n: %d, hash: %s\n", input.prevout.n, input.prevout.hash.GetHex().c_str());
printf("Notarization input n: %d, hash: %s\n", input.prevout.n, input.prevout.hash.GetHex().c_str());
}
rejects++;
}
}
}
if ( rejects == 0 || rejects == lastrejects )
{
if ( 0 && lastrejects != 0 )
if ( lastrejects != 0 )
{
fprintf(stderr,"lastrejects.%d -> all tx in mempool\n",lastrejects);
success = state.DoS(0, error("CheckBlock: invalid transactions in block"), REJECT_INVALID, "bad-txns-duplicate");
}
break;
}
//fprintf(stderr,"addtomempool ht.%d for CC checking: n.%d rejects.%d last.%d\n",height,(int32_t)block.vtx.size(),rejects,lastrejects);
@ -4655,36 +4748,45 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
//fprintf(stderr,"done putting block's tx into mempool\n");
}
for (uint32_t i = 0; i < block.vtx.size(); i++)
{
const CTransaction& tx = block.vtx[i];
if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,0) < 0 )
return error("CheckBlock: komodo_validate_interest failed");
if (!CheckTransaction(tx, state, verifier))
return error("CheckBlock: CheckTransaction failed");
}
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return state.DoS(100, error("CheckBlock: out-of-bounds SigOpCount"),
REJECT_INVALID, "bad-blk-sigops", true);
if ( komodo_check_deposit(height,block,(pindex==0||pindex->pprev==0)?0:pindex->pprev->nTime) < 0 )
if (success)
{
//static uint32_t counter;
//if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 )
// fprintf(stderr,"check deposit rejection\n");
LogPrintf("CheckBlockHeader komodo_check_deposit error");
return(false);
for (uint32_t i = 0; i < block.vtx.size(); i++)
{
const CTransaction& tx = block.vtx[i];
if ( komodo_validate_interest(tx,height == 0 ? komodo_block2height((CBlock *)&block) : height,block.nTime,0) < 0 )
{
success = error("CheckBlock: komodo_validate_interest failed");
}
if (success && !CheckTransaction(tx, state, verifier))
success = error("CheckBlock: CheckTransaction failed");
}
if (success)
{
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
nSigOps += GetLegacySigOpCount(tx);
}
if (nSigOps > MAX_BLOCK_SIGOPS)
success = state.DoS(100, error("CheckBlock: out-of-bounds SigOpCount"),
REJECT_INVALID, "bad-blk-sigops", true);
if ( success && komodo_check_deposit(height,block,(pindex==0||pindex->pprev==0)?0:pindex->pprev->nTime) < 0 )
{
LogPrintf("CheckBlock: komodo_check_deposit error");
success = error("CheckBlock: komodo_check_deposit error");
}
}
}
if (ptx)
if (!success)
{
// remove coinbase and anything that depended on it sooner, rather than later, if failure
RemoveCoinbaseFromMemPool(block);
} else if (ptx && fCheckTxInputs)
{
SyncWithWallets(*ptx, &block);
}
return true;
return success;
}
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev)
@ -5111,7 +5213,7 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo
LOCK(cs_main);
if ( chainActive.LastTip() != 0 )
komodo_currentheight_set(chainActive.LastTip()->GetHeight());
checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0);
checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock), 0, *pblock, state, verifier, 0, true, false);
bool fRequested = MarkBlockAsReceived(hash);
fRequested |= fForceProcessing;
if ( checked != 0 && komodo_checkPOW(0,pblock,height) < 0 ) //from_miner && ASSETCHAINS_STAKED == 0
@ -5125,6 +5227,7 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo
{
Misbehaving(pfrom->GetId(), 1);
}
RemoveCoinbaseFromMemPool(*pblock);
return error("%s: CheckBlock FAILED", __func__);
}
// Store to disk
@ -5135,15 +5238,36 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
CheckBlockIndex();
if (!ret && futureblock == 0)
{
RemoveCoinbaseFromMemPool(*pblock);
return error("%s: AcceptBlock FAILED", __func__);
}
//else fprintf(stderr,"added block %s %p\n",pindex->GetBlockHash().ToString().c_str(),pindex->pprev);
}
if (futureblock == 0 && !ActivateBestChain(state, pblock))
{
RemoveCoinbaseFromMemPool(*pblock);
return error("%s: ActivateBestChain failed", __func__);
}
//fprintf(stderr,"finished ProcessBlock %d\n",(int32_t)chainActive.LastTip()->GetHeight());
// submit notarization if there is one
if (!IsVerusActive() && pblock->vtx.size() > 1)
{
// if we made an earned notarization in the block,
// queue it to create an accepted notarization from it
// check coinbase and socond tx
CPBaaSNotarization pbncb(pblock->vtx[0]);
CPBaaSNotarization pbn(pblock->vtx[1]);
if (::GetHash(pbncb) == ::GetHash(pbn))
{
ConnectedChains.QueueEarnedNotarization(*pblock, 1, height);
}
}
// when we succeed here, we prune all cheat candidates in the cheat list to 250 blocks ago, as they should be used or not
// useful by then
if ((height - 250) > 1)
@ -5156,6 +5280,8 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
{
AssertLockHeld(cs_main);
assert(pindexPrev == chainActive.Tip());
bool success = false;
CCoinsViewCache viewNew(pcoinsTip);
CBlockIndex indexDummy(block);
@ -5164,31 +5290,18 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
// JoinSplit proofs are verified in ConnectBlock
auto verifier = libzcash::ProofVerifier::Disabled();
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
{
//fprintf(stderr,"TestBlockValidity failure A checkPOW.%d\n",fCheckPOW);
return false;
}
int32_t futureblock;
if (!CheckBlock(&futureblock,indexDummy.GetHeight(),0,block, state, verifier, fCheckPOW, fCheckMerkleRoot))
{
//fprintf(stderr,"TestBlockValidity failure B checkPOW.%d\n",fCheckPOW);
return false;
}
if (!ContextualCheckBlock(block, state, pindexPrev))
if (ContextualCheckBlockHeader(block, state, pindexPrev) &&
CheckBlock(&futureblock,indexDummy.GetHeight(),0,block, state, verifier, fCheckPOW, fCheckMerkleRoot) &&
ContextualCheckBlock(block, state, pindexPrev) &&
ConnectBlock(block, state, &indexDummy, viewNew, true,fCheckPOW) &&
futureblock == 0 )
{
//fprintf(stderr,"TestBlockValidity failure C checkPOW.%d\n",fCheckPOW);
return false;
success = true;
}
if (!ConnectBlock(block, state, &indexDummy, viewNew, true,fCheckPOW))
{
//fprintf(stderr,"TestBlockValidity failure D checkPOW.%d\n",fCheckPOW);
return false;
}
assert(state.IsValid());
if ( futureblock != 0 )
return(false);
return true;
RemoveCoinbaseFromMemPool(block);
return success;
}
/**
@ -5603,7 +5716,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->GetHeight(), pindex->GetBlockHash().ToString());
// check level 1: verify block validity
int32_t futureblock;
if (nCheckLevel >= 1 && !CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, verifier,0) )
if (nCheckLevel >= 1 && !CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, verifier, 0, true, false) )
return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->GetHeight(), pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
@ -5644,7 +5757,10 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
if (!ReadBlockFromDisk(block, pindex,0))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->GetHeight(), pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins,false, true))
{
RemoveCoinbaseFromMemPool(block);
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->GetHeight(), pindex->GetBlockHash().ToString());
}
}
}
@ -6928,6 +7044,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->setAskFor.erase(inv.hash);
mapAlreadyAskedFor.erase(inv);
// coinbases would be accepted to the mem pool for instant spend transactions, stop them here
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
{
mempool.check(pcoinsTip);
@ -7332,7 +7449,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
vector<unsigned char> vData;
vRecv >> vData;
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// Nodes must NEVER send a data item bigger than the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE)
{

9
src/main.h

@ -239,6 +239,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, b
/** Find the best known block, and make it the tip of the block chain */
bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL);
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
CAmount GetBlockOnePremine();
/**
* Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
@ -276,6 +277,8 @@ 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 AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectAbsurdFee=false, int dosLevel=-1, int32_t simHeight = 0);
struct CNodeStateStats {
@ -494,10 +497,6 @@ enum ADDRESS_INDEX_QUERY_CODES {
ADDRESSINDEX_OFFERLIMIT = 0x83 // for good until cancelled and fill or kill (anti-front running) limit orders
};
enum PBAAS_CONSTANTS {
PBAAS_NOTARYCONFIRMS = 20 // number of confirms necessary to consider a notarization valid
};
struct CUniqueHash
{
uint256 hash;
@ -985,7 +984,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
libzcash::ProofVerifier& verifier,
bool fCheckPOW = true, bool fCheckMerkleRoot = true);
bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckTxInputs = true);
/** Context-dependent validity checks */
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev);

388
src/miner.cpp

@ -49,6 +49,7 @@
#include "pbaas/pbaas.h"
#include "pbaas/notarization.h"
#include "transaction_builder.h"
using namespace std;
@ -335,6 +336,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
if (cheatSpend)
{
LOCK2(cs_main, mempool.cs);
cheatTx = cheatSpend.value();
std::list<CTransaction> removed;
mempool.removeConflicts(cheatTx, removed);
@ -350,70 +352,67 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// if we are a PBaaS chain, first make sure we don't start prematurely, and if
// we should make an earned notarization, make it and set index to non-zero value
int32_t pbaasNotarizationTx = 0;
int32_t pbaasNotarizationTx = 0; // index of transaction if it is added
int64_t pbaasTransparentIn = 0;
int64_t pbaasTransparentOut = 0;
int64_t blockSubsidy = GetBlockSubsidy(nHeight, consensusParams);
uint256 mmrRoot;
if (!IsVerusActive())
vector<CInputDescriptor> notarizationInputs;
// make earned notarization only if this is not the notary chain and we have enough subsidy
//
// TODO: allow this to proceed if no subsidy and earned notarizations pay no reward as well
// we will need to provide a notarization reward option, even for non-fungible chains, just to enable
// creation of the notarizations
if (!IsVerusActive() && (blockSubsidy > PBAAS_MINNOTARIZATIONOUTPUT * 2))
{
// if we don't have a connected root PBaaS chain, we can't properly check
// and notarize the start block, so we have to pass and wait
if (nHeight != 1 || (ConnectedChains.IsVerusPBaaSAvailable() && ConnectedChains.notaryChainHeight >= PBAAS_STARTBLOCK))
{
// if we have access to our parent daemon
// create a notarization, if we would qualify, and add it to the mempool and block
// create a notarization if we would qualify, and add it to the mempool and block
CMutableTransaction newNotarizationTx;
CTransaction prevTx, crossTx;
ChainMerkleMountainView mmv = chainActive.GetMMV();
mmrRoot = mmv.GetRoot();
if (CreateEarnedNotarization(newNotarizationTx, prevTx, crossTx, nHeight, mmrRoot))
int32_t confirmedInput = -1;
CTxDestination confirmedDest;
if (CreateEarnedNotarization(newNotarizationTx, notarizationInputs, prevTx, crossTx, nHeight, &confirmedInput, &confirmedDest))
{
// we have a valid, earned notarization transaction. we still need to verify:
// 1. it has matching input for its outputs, since it can be returned with less than enough,
// if there is not enough, take it as instant-spend from the coinbase. if there is too much,
// increase the output on the main notarization thread.
// instant-spend and notarization
// transaction threads on a PBaaS chain can only be used for notarization, and can never convert to
// available supply
// Fetch previous transactions (inputs):
// we have a valid, earned notarization transaction. we still need to complete it as follows:
// 1. Add an instant-spend input from the coinbase transaction to fund the finalization output
//
// 2. if we are spending finalization outputs, create an output of the same amount as a finalization output
// plus and any excess from the other, orphaned finalizations to the creator of the confirmed notarization
//
// input should either be 0 or PBAAS_MINNOTARIZATIONOUTPUT + all finalized outputs
// we will add PBAAS_MINNOTARIZATIONOUTPUT from a coinbase instant spend in all cases and double that when in is 0 for block 1
for (const CTxIn& txin : newNotarizationTx.vin)
{
const uint256& prevHash = txin.prevout.hash;
const CCoins *pcoins = view.AccessCoins(prevHash); // this is certainly allowed to fail
const CCoins *pcoins = view.AccessCoins(prevHash);
pbaasTransparentIn += pcoins && (pcoins->vout.size() > txin.prevout.n) ? pcoins->vout[txin.prevout.n].nValue : 0;
}
for (auto txout : newNotarizationTx.vout)
// calculate the amount that will be sent to the confirmed notary address
// this will only be non-zero if we have finalized inputs
if (pbaasTransparentIn > 0)
{
pbaasTransparentOut += txout.nValue;
pbaasTransparentOut = pbaasTransparentIn - PBAAS_MINNOTARIZATIONOUTPUT;
}
if (pbaasTransparentOut < pbaasTransparentIn)
if (pbaasTransparentOut)
{
// add excess to the notarization output
int notarizeOut = -1;
for (int outIdx = 0; outIdx < newNotarizationTx.vout.size(); outIdx++)
{
uint32_t ecode;
if (newNotarizationTx.vout[outIdx].scriptPubKey.IsPayToCryptoCondition(&ecode))
{
if (ecode == EVAL_EARNEDNOTARIZATION)
{
newNotarizationTx.vout[outIdx].nValue += pbaasTransparentIn - pbaasTransparentOut;
break;
}
}
}
// if we are on a non-fungible chain, reward out must be unspendable
// make a normal output to the confirmed notary with the excess right behind the op_return
// TODO: make this a cc out to only allow spending on a fungible chain
CTxOut rewardOut = CTxOut(pbaasTransparentOut, GetScriptForDestination(confirmedDest));
newNotarizationTx.vout.insert(newNotarizationTx.vout.begin() + newNotarizationTx.vout.size() - 1, rewardOut);
}
if (pbaasTransparentOut > pbaasTransparentIn)
{
// add a non-fungible input to bind the notarization to the block, specific to block height, previous MMR
// output will be added to coinbase with the same notarization output as well
newNotarizationTx.vin.push_back(CTxIn(::GetHash(CPBaaSNotarization(newNotarizationTx)), 0));
}
pblock->vtx.push_back(CTransaction(newNotarizationTx));
pblocktemplate->vTxFees.push_back(0);
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
@ -424,6 +423,10 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// failed to notarize at block 1
return NULL;
}
else
{
printf("Skip creating earned notarization\n");
}
}
else
{
@ -701,7 +704,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
txNew.vout.resize(1);
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees;
txNew.vout[0].nValue = blockSubsidy + nFees;
// once we get to Sapling, enable CC StakeGuard for stake transactions
if (isStake && sapling)
@ -723,6 +726,17 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
return 0;
}
}
else if (!IsVerusActive() && stakeHeight == 1)
{
// first block, send normal reward to the miner, and premine to the specified address in the
// chain definition
if (!ConnectedChains.ThisChain().address.IsNull() && GetBlockOnePremine())
{
// move miner output to output 1 and put premine in output 0
txNew.vout.push_back(CTxOut(txNew.vout[0].nValue - GetBlockOnePremine(), txNew.vout[0].scriptPubKey));
txNew.vout[0] = CTxOut(GetBlockOnePremine(), GetScriptForDestination(CTxDestination(ConnectedChains.ThisChain().address)));
}
}
txNew.nExpiryHeight = 0;
txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
@ -732,12 +746,17 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// check if coinbase transactions must be time locked at current subsidy and prepend the time lock
// to transaction if so, cast for GTE operator
if ((uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE)
CAmount cbValueOut = 0;
for (auto txout : txNew.vout)
{
cbValueOut += txout.nValue;
}
if (cbValueOut >= ASSETCHAINS_TIMELOCKGTE)
{
int32_t opretlen, p2shlen, scriptlen;
CScriptExt opretScript = CScriptExt();
txNew.vout.resize(2);
txNew.vout.push_back(CTxOut());
// prepend time lock to original script unless original script is P2SH, in which case, we will leave the coins
// protected only by the time lock rather than 100% inaccessible
@ -751,8 +770,8 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
opretScript += scriptPubKeyIn;
txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript));
txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK);
txNew.vout[1].nValue = 0;
txNew.vout.back().scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK);
txNew.vout.back().nValue = 0;
} // timelocks and commissions are currently incompatible due to validation complexity of the combination
else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block)) != 0 )
{
@ -768,6 +787,10 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
//printf("autocreate commision vout\n");
}
// finalize input of coinbase
txNew.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(0)) + COINBASE_FLAGS;
assert(txNew.vin[0].scriptSig.size() <= 100);
// add final notarization and instant spend fixups
if (pbaasNotarizationTx)
{
@ -779,51 +802,39 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
// determine number of outputs
int numNotaryOutputs = mntx.vout.size() - (mntx.vout[mntx.vout.size() - 1].scriptPubKey.IsOpReturn() ? 1 : 0);
int64_t needed = pbaasTransparentOut - pbaasTransparentIn;
if (needed > PBAAS_MINNOTARIZATIONOUTPUT * numNotaryOutputs)
{
fprintf(stderr,"CreateNewBlock: too much output from earned notarization transaction\n");
return NULL;
}
// either minimum or minimum times two for block 1
int64_t needed = mntx.vin.size() == 0 ? PBAAS_MINNOTARIZATIONOUTPUT << 1 : PBAAS_MINNOTARIZATIONOUTPUT;
int32_t pbaasCoinbaseInstantSpendOut;
// if we need an instant out to be a source of funds for the notarization transaction, make it here
if (needed > 0)
{
// the new instant spend out will go at the end and before any opret
pbaasCoinbaseInstantSpendOut = txNew.vout.size() - (txNew.vout[txNew.vout.size() - 1].scriptPubKey.IsOpReturn() ? 1 : 0);
// the new instant spend out will go at the end and before any opret
pbaasCoinbaseInstantSpendOut = txNew.vout.size() - (txNew.vout[txNew.vout.size() - 1].scriptPubKey.IsOpReturn() ? 1 : 0);
auto coinbaseOutIt = txNew.vout.begin() + pbaasCoinbaseInstantSpendOut;
auto coinbaseOutIt = txNew.vout.begin() + pbaasCoinbaseInstantSpendOut;
CCcontract_info CC;
CCcontract_info *cp;
vector<CTxDestination> vKeys;
CCcontract_info CC;
CCcontract_info *cp;
vector<CTxDestination> vKeys;
// make the earned notarization output
cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
// send this to EVAL_EARNEDNOTARIZATION address as a destination, locked by the default pubkey
CPubKey pk(ParseHex(cp->CChexstr));
// make the earned notarization output
cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
// send this to EVAL_EARNEDNOTARIZATION address as a destination, locked by the default pubkey
CPubKey pk(ParseHex(cp->CChexstr));
vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
// output duplicate notarization as coinbase output for instant spend to notarization
// the output is 0 and will be used to match, not to spend, so it does not need to be considered as
// part of the total value of this coinbase
CPBaaSNotarization pbn(pblock->vtx[pbaasNotarizationTx]);
txNew.vout.insert(coinbaseOutIt, MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, 0, pk, vKeys, pbn));
pblock->vtx[0] = txNew;
// output duplicate notarization as coinbase output for instant spend to notarization
// the output amount is considered part of the total value of this coinbase
CPBaaSNotarization pbn(pblock->vtx[pbaasNotarizationTx]);
txNew.vout.insert(coinbaseOutIt, MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, needed, pk, vKeys, pbn));
txNew.vout[0].nValue = txNew.vout[0].nValue - needed;
// bind to the right output of the coinbase
mntx.vin[mntx.vin.size() - 1].prevout.n = pbaasCoinbaseInstantSpendOut;
// put notarization back in the block
pblock->vtx[pbaasNotarizationTx] = mntx;
}
// bind to the right output of the coinbase
mntx.vin.push_back(CTxIn(txNew.GetHash(), pbaasCoinbaseInstantSpendOut));
uint256 cbHash = mntx.vin[mntx.vin.size() - 1].prevout.hash;
CTransaction ntx(mntx);
for (int i = 0, endat = (needed > 0 ? ntx.vin.size() - 1 : ntx.vin.size()); i < endat; i++)
for (int i = 0; i < ntx.vin.size(); i++)
{
bool signSuccess;
SignatureData sigdata;
@ -833,28 +844,16 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
const CScript virtualCC;
CTxOut virtualCCOut;
if (needed > 0 && mmrRoot == ntx.vin[i].prevout.hash && nHeight == ntx.vin[i].prevout.n)
// if this is our coinbase input, we won't find it elsewhere
if (i < notarizationInputs.size())
{
CCcontract_info CC;
CCcontract_info *cp;
vector<CTxDestination> vKeys;
// make the earned notarization output, but don't keep it
// on validation, we can ensure that an accurate notarization was spent as
cp = CCinit(&CC, EVAL_EARNEDNOTARIZATION);
CPubKey pk(ParseHex(cp->CChexstr));
vKeys.push_back(CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(VERUS_CHAINID, EVAL_EARNEDNOTARIZATION))));
CPBaaSNotarization pbn(pblock->vtx[pbaasNotarizationTx]);
virtualCCOut = MakeCC1of1Vout(EVAL_EARNEDNOTARIZATION, needed, pk, vKeys, pbn);
pScriptPubKey = &virtualCCOut.scriptPubKey;
value = virtualCCOut.nValue;
pScriptPubKey = &notarizationInputs[i].scriptPubKey;
value = notarizationInputs[i].nValue;
}
else
{
const CCoins *coins = view.AccessCoins(ntx.vin[i].prevout.hash);
pScriptPubKey = &coins->vout[ntx.vin[i].prevout.n].scriptPubKey;
value = coins->vout[ntx.vin[i].prevout.n].nValue;
pScriptPubKey = &txNew.vout[ntx.vin[i].prevout.n].scriptPubKey;
value = txNew.vout[ntx.vin[i].prevout.n].nValue;
}
signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
@ -868,6 +867,27 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
}
}
pblocktemplate->vTxSigOps[pbaasNotarizationTx] = GetLegacySigOpCount(mntx);
// put now signed notarization back in the block
pblock->vtx[pbaasNotarizationTx] = mntx;
LogPrintf("Coinbase source tx id: %s\n", txNew.GetHash().GetHex().c_str());
printf("Coinbase source tx id: %s\n", txNew.GetHash().GetHex().c_str());
LogPrintf("adding notarization tx at height %d, index %d, id: %s\n", nHeight, pbaasNotarizationTx, mntx.GetHash().GetHex().c_str());
printf("adding notarization tx at height %d, index %d, id: %s\n", nHeight, pbaasNotarizationTx, mntx.GetHash().GetHex().c_str());
{
LOCK(cs_main);
for (auto input : mntx.vin)
{
LogPrintf("Earned notarization input n: %d, hash: %s, HaveCoins: %s\n", input.prevout.n, input.prevout.hash.GetHex().c_str(), pcoinsTip->HaveCoins(input.prevout.hash) ? "true" : "false");
printf("Earned notarization input n: %d, hash: %s\n", input.prevout.n, input.prevout.hash.GetHex().c_str(), pcoinsTip->HaveCoins(input.prevout.hash) ? "true" : "false");
}
}
for (auto inp : mntx.vin)
{
printf("Spending n: %d, hash: %s\n", inp.prevout.n, inp.prevout.hash.GetHex().c_str());
}
}
pblock->vtx[0] = txNew;
@ -996,7 +1016,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
#ifdef ENABLE_MINING
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int &nExtraNonce, bool buildMerkle, uint32_t *pSaveBits)
{
// Update nExtraNonce
static uint256 hashPrevBlock;
@ -1006,15 +1026,84 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
unsigned int nHeight = pindexPrev->GetHeight()+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txCoinbase;
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
if (pSaveBits)
{
*pSaveBits = pblock->nBits;
}
int32_t nHeight = pindexPrev->GetHeight() + 1;
if (CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) >= CConstVerusSolutionVector::activationHeight.SOLUTION_VERUSV3)
{
// coinbase should already be finalized in the new version
if (buildMerkle)
{
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
uint256 mmvRoot;
{
LOCK(cs_main);
// set the PBaaS header
ChainMerkleMountainView mmv = chainActive.GetMMV();
mmvRoot = mmv.GetRoot();
}
pblock->AddUpdatePBaaSHeader(mmvRoot);
// POS blocks have already had their solution space filled, and there is no actual extra nonce, extradata is used
// for POS proof, so don't modify it
if (!pblock->IsVerusPOSBlock())
{
uint8_t dummy;
// clear extra data to allow adding more PBaaS headers
pblock->SetExtraData(&dummy, 0);
// combine blocks and set compact difficulty if necessary
uint32_t savebits;
if ((savebits = ConnectedChains.CombineBlocks(*pblock)) && pSaveBits)
{
if (pSaveBits)
{
arith_uint256 ours, merged;
ours.SetCompact(pblock->nBits);
merged.SetCompact(*pSaveBits);
if (merged > ours)
{
*pSaveBits = savebits;
}
}
}
// extra nonce is kept in the header, not in the coinbase any longer
// this allows instant spend transactions to use coinbase funds for
// inputs by ensuring that once final, the coinbase transaction hash
// will not continue to change
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
s << nExtraNonce;
std::vector<unsigned char> vENonce(s.begin(), s.end());
assert(pblock->ExtraDataLen() >= vENonce.size());
pblock->SetExtraData(vENonce.data(), vENonce.size());
}
}
else
{
// finalize input of coinbase
CMutableTransaction txcb(pblock->vtx[0]);
txcb.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txcb.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txcb;
if (buildMerkle)
{
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
}
}
#ifdef ENABLE_WALLET
@ -1081,8 +1170,9 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
static bool ProcessBlockFound(CBlock* pblock)
#endif // ENABLE_WALLET
{
int32_t height = chainActive.LastTip()->GetHeight()+1;
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue),chainActive.LastTip()->GetHeight()+1);
LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue), height);
// Found a solution
{
@ -1310,23 +1400,10 @@ void static VerusStaker(CWallet *pwallet)
//
int64_t nStart = GetTime();
// we don't use this, but IncrementExtraNonce is the function that builds the merkle tree
// IncrementExtraNonce also builds the merkle tree
unsigned int nExtraNonce = 0;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
// update PBaaS header
if (CConstVerusSolutionVector::activationHeight.ActiveVersion(Mining_height) == CActivationHeight::SOLUTION_VERUSV3)
{
uint256 mmvRoot;
{
LOCK(cs_main);
// set the PBaaS header
ChainMerkleMountainView mmv = chainActive.GetMMV();
mmvRoot = mmv.GetRoot();
}
pblock->AddUpdatePBaaSHeader(mmvRoot);
}
if (vNodes.empty() && chainparams.MiningRequiresPeers())
{
if ( Mining_height > ASSETCHAINS_MINHEIGHT )
@ -1358,22 +1435,28 @@ void static VerusStaker(CWallet *pwallet)
UpdateTime(pblock, consensusParams, pindexPrev);
ProcessBlockFound(pblock, *pwallet, reservekey);
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);
arith_uint256 post;
post.SetCompact(pblock->GetVerusPOSTarget());
pindexPrev = get_chainactive(Mining_height - 100);
CTransaction &sTx = pblock->vtx[pblock->vtx.size()-1];
printf("POS hash: %s \ntarget: %s\n",
CTransaction::_GetVerusPOSHash(&(pblock->nNonce), sTx.vin[0].prevout.hash, sTx.vin[0].prevout.n, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), sTx.vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
printf("- timelocked until block %i\n", unlockTime);
if (ProcessBlockFound(pblock, *pwallet, reservekey))
{
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);
arith_uint256 post;
post.SetCompact(pblock->GetVerusPOSTarget());
pindexPrev = get_chainactive(Mining_height - 100);
CTransaction &sTx = pblock->vtx[pblock->vtx.size()-1];
printf("POS hash: %s \ntarget: %s\n",
CTransaction::_GetVerusPOSHash(&(pblock->nNonce), sTx.vin[0].prevout.hash, sTx.vin[0].prevout.n, Mining_height, pindexPrev->GetBlockHeader().GetVerusEntropyHash(Mining_height - 100), sTx.vout[0].nValue).GetHex().c_str(), ArithToUint256(post).GetHex().c_str());
if (unlockTime > Mining_height && subsidy >= ASSETCHAINS_TIMELOCKGTE)
printf("- timelocked until block %i\n", unlockTime);
else
printf("\n");
}
else
printf("\n");
{
LogPrintf("Found block rejected at staking height: %d\n", Mining_height);
printf("Found block rejected at staking height: %d\n", Mining_height);
}
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
@ -1496,8 +1579,21 @@ void static BitcoinMiner_noeq()
if ( ptr == 0 )
{
static uint32_t counter;
if ( (counter++ < 10) || (counter % 40 == 0) )
fprintf(stderr,"Unable to create valid block... will continue to try\n");
if ( counter++ % 40 == 0 )
{
if (!IsVerusActive() &&
ConnectedChains.IsVerusPBaaSAvailable() &&
ConnectedChains.notaryChainHeight < ConnectedChains.ThisChain().startBlock)
{
fprintf(stderr,"Waiting for block %d on %s chain to start. Current block is %d\n", ConnectedChains.ThisChain().startBlock,
ConnectedChains.notaryChain.chainDefinition.name.c_str(),
ConnectedChains.notaryChainHeight);
}
else
{
fprintf(stderr,"Unable to create valid block... will continue to try\n");
}
}
MilliSleep(2000);
continue;
}
@ -1539,19 +1635,11 @@ void static BitcoinMiner_noeq()
}
// this builds the Merkle tree
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, true, &savebits);
// update PBaaS header
if (verusSolutionV3)
{
uint256 mmvRoot;
{
LOCK(cs_main);
ChainMerkleMountainView mmv = chainActive.GetMMV();
mmvRoot = mmv.GetRoot();
}
pblock->AddUpdatePBaaSHeader(mmvRoot);
if (!IsVerusActive() && ConnectedChains.IsVerusPBaaSAvailable())
{
@ -1686,7 +1774,6 @@ void static BitcoinMiner_noeq()
if ((mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && elapsed > 60) || elapsed > 60 || ConnectedChains.lastSubmissionFailed)
{
break;
}
@ -1708,12 +1795,10 @@ void static BitcoinMiner_noeq()
do
{
// pickup/remove any new/deleted headers
if (ConnectedChains.dirty)
if (ConnectedChains.dirty || (pblock->NumPBaaSHeaders() < ConnectedChains.mergeMinedChains.size() + 1))
{
if (!(savebits = ConnectedChains.CombineBlocks(*pblock)))
{
savebits = pblock->nBits;
}
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, false, &savebits);
hashTarget.SetCompact(savebits);
uintTarget = ArithToUint256(hashTarget);
}
@ -1727,6 +1812,7 @@ void static BitcoinMiner_noeq()
{
// mine on canonical header for merge mining
CPBaaSPreHeader savedHeader(*pblock);
pblock->ClearNonCanonicalData();
blockFound = (*mine_verus)(*pblock, ss2, hashResult, uintTarget, start, &hashesToGo);
savedHeader.SetBlockData(*pblock);

2
src/miner.h

@ -39,7 +39,7 @@ CBlockTemplate* CreateNewBlockWithKey();
#ifdef ENABLE_MINING
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, bool buildMerkle = true, uint32_t *pSaveBits = NULL);
/** Run the miner threads */
#ifdef ENABLE_WALLET
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads);

2
src/mmr.h

@ -635,7 +635,7 @@ public:
uint256 root = GetRoot();
if (!root.IsNull())
{
return &(peakMerkle[peakMerkle.size() - 1][0]);
return &(peakMerkle.back()[0]);
}
else
{

6
src/net.cpp

@ -447,7 +447,11 @@ void CNode::PushVersion()
CKeyID nodePaymentAddress;
if (USE_EXTERNAL_PUBKEY)
{
nodePaymentAddress = CKeyID(uint160((ParseHex(NOTARY_PUBKEY))));
CPubKey pubKey = CPubKey(ParseHex(NOTARY_PUBKEY));
if (pubKey.IsFullyValid())
{
nodePaymentAddress = pubKey.GetID();
}
LogPrint("net", "send PBaaS node payment pubkey hash -- pubkey: %s, hash: %s\n", NOTARY_PUBKEY, nodePaymentAddress.ToString());
}
PushMessage("version", PROTOCOL_VERSION > MIN_PBAAS_VERSION ? PROTOCOL_VERSION : MIN_PBAAS_VERSION,

4
src/pbaas/crosschainrpc.cpp

@ -296,8 +296,6 @@ uint160 CCrossChainRPCData::GetConditionID(uint160 cid, int32_t condition)
hw << condition;
hw << cid;
uint256 chainHash = hw.GetHash();
uint160 output = Hash160(chainHash.begin(), chainHash.end());
return Hash160(chainHash.begin(), chainHash.end());
}
@ -309,7 +307,5 @@ uint160 CCrossChainRPCData::GetConditionID(std::string name, int32_t condition)
hw << condition;
hw << cid;
uint256 chainHash = hw.GetHash();
uint160 output = Hash160(chainHash.begin(), chainHash.end());
return Hash160(chainHash.begin(), chainHash.end());
}

870
src/pbaas/notarization.cpp

File diff suppressed because it is too large

44
src/pbaas/notarization.h

@ -69,6 +69,12 @@
// Part of a transaction with an opret that contains only the hashes and proofs, without the source
// headers, transactions, and objects. This type of notarizatoin is mined into a block by the miner, and is created on the PBaaS
// chain.
//
// Notarizations include the following elements in order:
// Latest block header being notarized, or a header ref for a merge-mined header
// Proof of the header using the latest MMR root
// Cross notarization transaction less its op_ret
// Proof of the cross notarization using the latest MMR root
class CPBaaSNotarization
{
public:
@ -77,15 +83,16 @@ public:
static const int CURRENT_VERSION = PBAAS_VERSION;
uint32_t nVersion; // PBAAS version
uint160 chainID; // chain being notarized
CAmount rewardPerBlock; // amount to pay per reward, cannot be changed under normal circumstances
uint160 notaryKeyID; // confirmed notary rewards are spent to this address when this notarization is confirmed
uint32_t notarizationHeight; // height of the notarization we certify
uint256 mmrRoot; // latest MMR root of the notarization height
uint256 notarizationPreHash; // combination of block hash, merkle root, and compact power for the notarization height
uint256 compactPower; // compact power of the block height notarization to compare
uint256 prevNotarization; // txid of the prior notarization on this chain that we agree with, even those not accepted yet
int32_t prevHeight;
uint256 crossNotarization; // hash of last notarization transaction on the other chain, which is the first tx object in the opret input
uint256 crossNotarization; // hash of previous notarization transaction on the other chain, which is the first tx object in the opret input
int32_t crossHeight;
COpRetProof opRetProof; // hashes and types of all objects in our opret, enabling reconstruction without the opret on notarized chain
@ -96,9 +103,10 @@ public:
CPBaaSNotarization(uint32_t version,
uint160 chainid,
CAmount rewardperblock,
uint160 notarykey,
int32_t notarizationheight,
uint256 MMRRoot,
uint256 MMRRoot,
uint256 preHash,
uint256 compactpower,
uint256 prevnotarization,
int32_t prevheight,
@ -108,10 +116,11 @@ public:
std::vector<CNodeData> &Nodes) :
nVersion(version),
chainID(chainid),
rewardPerBlock(rewardperblock),
notaryKeyID(notarykey),
notarizationHeight(notarizationheight),
mmrRoot(MMRRoot),
notarizationPreHash(preHash),
compactPower(compactpower),
prevNotarization(prevnotarization),
@ -139,9 +148,10 @@ public:
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(nVersion));
READWRITE(chainID);
READWRITE(rewardPerBlock);
READWRITE(notaryKeyID);
READWRITE(notarizationHeight);
READWRITE(mmrRoot);
READWRITE(notarizationPreHash);
READWRITE(compactPower);
READWRITE(prevNotarization);
READWRITE(prevHeight);
@ -252,14 +262,34 @@ public:
UniValue ToUniValue() const;
};
bool CreateEarnedNotarization(CMutableTransaction &mnewTx, CTransaction &lastTx, CTransaction &crossTx, int32_t height, uint256 &prevMMR);
class CInputDescriptor
{
public:
CScript scriptPubKey;
CAmount nValue;
CTxIn txIn;
CInputDescriptor() : nValue(0) {}
CInputDescriptor(CScript script, CAmount value, CTxIn input) : scriptPubKey(script), nValue(value), txIn(input) {}
};
bool CreateEarnedNotarization(CMutableTransaction &mnewTx, std::vector<CInputDescriptor> &inputs, CTransaction &lastTx, CTransaction &crossTx, int32_t height, int32_t *confirmedInput, CTxDestination *confirmedDest);
uint256 CreateAcceptedNotarization(const CBlock &blk, int32_t txIndex, int32_t height);
std::vector<CInputDescriptor> AddSpendsAndFinalizations(const CChainNotarizationData &cnd,
const uint256 &lastNotarizationID,
const CTransaction &lastTx,
CMutableTransaction &mnewTx,
int32_t *pConfirmedInput,
int32_t *pConfirmedIdx,
CTxDestination *pConfirmedDest);
bool GetNotarizationAndFinalization(int32_t ecode, CMutableTransaction mtx, CPBaaSNotarization &pbn, uint32_t *pNotarizeOutIndex, uint32_t *pFinalizeOutIndex);
bool ValidateEarnedNotarization(CTransaction &ntx, CPBaaSNotarization *notarization = NULL);
bool ValidateEarnedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool IsEarnedNotarizationInput(const CScript &scriptSig);
bool ValidateAcceptedNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool IsAcceptedNotarizationInput(const CScript &scriptSig);
bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool IsFinalizeNotarizationInput(const CScript &scriptSig);
bool IsServiceRewardInput(const CScript &scriptSig);
bool IsBlockBoundTransaction(const CTransaction &tx);
#endif

291
src/pbaas/pbaas.cpp

@ -16,6 +16,7 @@
#include "pbaas/crosschainrpc.h"
#include "base58.h"
#include "timedata.h"
#include "main.h"
using namespace std;
@ -52,8 +53,7 @@ uint256 GetChainObjectHash(const CBaseChainObject &bo)
const CChainObject<CTransaction> *pNewTx;
const CChainObject<CMerkleBranch> *pNewProof;
const CChainObject<CHeaderRef> *pNewHeaderRef;
const CChainObject<CTransactionRef> *pNewTxRef;
const CChainObject<COpRetRef> *pNewOpRetRef;
const CChainObject<CPriorBlocksCommitment> *pPriors;
const CBaseChainObject *retPtr;
};
@ -73,11 +73,8 @@ uint256 GetChainObjectHash(const CBaseChainObject &bo)
case CHAINOBJ_HEADER_REF:
return pNewHeaderRef->GetHash();
case CHAINOBJ_TRANSACTION_REF:
return pNewTxRef->GetHash();
case CHAINOBJ_OPRET_REF:
return pNewOpRetRef->GetHash();
case CHAINOBJ_PRIORBLOCKS:
return pPriors->GetHash();
}
return uint256();
}
@ -99,7 +96,11 @@ bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransact
// used to validate a specific service reward based on the spending transaction
bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
{
// for each type of service reward, we need to check and see if the spender is
// correctly formatted to be a valid spend of the service reward. for notarization
// we ensure that the notarization and its outputs are valid and that the spend
// applies to the correct billing period
return true;
}
// used as a proxy token output for a reserve currency on its fractional reserve chain
@ -136,39 +137,29 @@ bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
}
int8_t ObjTypeCode(COpRetProof &obj)
{
return CHAINOBJ_OPRETPROOF;
}
int8_t ObjTypeCode(CBlockHeader &obj)
int8_t ObjTypeCode(const CBlockHeader &obj)
{
return CHAINOBJ_HEADER;
}
int8_t ObjTypeCode(CMerkleBranch &obj)
int8_t ObjTypeCode(const CMerkleBranch &obj)
{
return CHAINOBJ_PROOF;
}
int8_t ObjTypeCode(CTransaction &obj)
int8_t ObjTypeCode(const CTransaction &obj)
{
return CHAINOBJ_TRANSACTION;
}
int8_t ObjTypeCode(CHeaderRef &obj)
int8_t ObjTypeCode(const CHeaderRef &obj)
{
return CHAINOBJ_HEADER_REF;
}
int8_t ObjTypeCode(CTransactionRef &obj)
int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
{
return CHAINOBJ_TRANSACTION_REF;
}
int8_t ObjTypeCode(COpRetRef &obj)
{
return CHAINOBJ_OPRET_REF;
return CHAINOBJ_PRIORBLOCKS;
}
// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
@ -176,22 +167,74 @@ CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
{
CScript vData;
CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
s << (int32_t)OPRETTYPE_OBJECTARR;
bool error = false;
for (auto pobj : objPtrs)
{
if (!DehydrateChainObject(s, pobj))
try
{
if (!DehydrateChainObject(s, pobj))
{
error = true;
break;
}
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
error = true;
break;
}
}
//std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
//printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
std::vector<unsigned char> vch(s.begin(), s.end());
return error ? CScript() : CScript() << OP_RETURN << vch;
}
void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
{
for (auto pobj : ora)
{
switch(pobj->objectType)
{
case CHAINOBJ_HEADER:
{
delete (CChainObject<CBlockHeader> *)pobj;
break;
}
case CHAINOBJ_TRANSACTION:
{
delete (CChainObject<CTransaction> *)pobj;
break;
}
case CHAINOBJ_PROOF:
{
delete (CChainObject<CMerkleBranch> *)pobj;
break;
}
case CHAINOBJ_HEADER_REF:
{
delete (CChainObject<CHeaderRef> *)pobj;
break;
}
vData << OPRETTYPE_OBJECTARR << vch;
vch = std::vector<unsigned char>(vData.begin(), vData.end());
return CScript() << OP_RETURN << vch;
case CHAINOBJ_PRIORBLOCKS:
{
delete (CChainObject<CPriorBlocksCommitment> *)pobj;
break;
}
default:
delete pobj;
}
}
}
std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
@ -202,10 +245,31 @@ std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
{
CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
CBaseChainObject *pobj;
while (!s.empty() && (pobj = RehydrateChainObject(s)))
int32_t opRetType;
try
{
vRet.push_back(pobj);
s >> opRetType;
if (opRetType == OPRETTYPE_OBJECTARR)
{
CBaseChainObject *pobj;
while (!s.empty() && (pobj = RehydrateChainObject(s)))
{
vRet.push_back(pobj);
}
if (!s.empty())
{
printf("failed to load all objects in opret");
DeleteOpRetObjects(vRet);
vRet.clear();
}
}
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
DeleteOpRetObjects(vRet);
vRet.clear();
}
}
return vRet;
@ -214,14 +278,15 @@ std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
CNodeData::CNodeData(UniValue &obj)
{
networkAddress = uni_get_str(find_value(obj, "networkaddress"));
paymentAddress = uni_get_str(find_value(obj, "paymentaddress"));
CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
ba.GetKeyID(paymentAddress);
}
UniValue CNodeData::ToUniValue() const
{
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("networkaddress", networkAddress));
obj.push_back(Pair("paymentaddress", paymentAddress));
obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
return obj;
}
@ -229,7 +294,8 @@ CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
{
nVersion = PBAAS_VERSION;
name = uni_get_str(find_value(obj, "name"));
address = uni_get_str(find_value(obj, "address"));
CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
ba.GetKeyID(address);
premine = uni_get_int64(find_value(obj, "premine"));
conversion = uni_get_int64(find_value(obj, "conversion"));
launchFee = uni_get_int64(find_value(obj, "launchfee"));
@ -268,10 +334,10 @@ CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool valida
nVersion = PBAAS_VERSION_INVALID;
for (auto out : tx.vout)
{
uint32_t ecode;
if (out.scriptPubKey.IsPayToCryptoCondition(&ecode))
COptCCParams p;
if (IsPayToCryptoCondition(out.scriptPubKey, p))
{
if (ecode == EVAL_PBAASDEFINITION)
if (p.evalCode == EVAL_PBAASDEFINITION)
{
if (definitionFound)
{
@ -279,12 +345,8 @@ CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool valida
}
else
{
COptCCParams p;
FromVector(p.vData[0], *this);
definitionFound = true;
if (!IsPayToCryptoCondition(out.scriptPubKey, p, *this))
{
nVersion = PBAAS_VERSION_INVALID;
}
}
}
}
@ -296,6 +358,30 @@ CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool valida
}
}
CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
{
nVersion = PBAAS_VERSION_INVALID;
for (auto out : tx.vout)
{
COptCCParams p;
if (IsPayToCryptoCondition(out.scriptPubKey, p))
{
// always take the first for now
if (p.evalCode == EVAL_SERVICEREWARD)
{
FromVector(p.vData[0], *this);
break;
}
}
}
if (validate)
{
}
}
uint160 CPBaaSChainDefinition::GetChainID(std::string name)
{
const char *chainName = name.c_str();
@ -313,7 +399,7 @@ UniValue CPBaaSChainDefinition::ToUniValue() const
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("version", (int64_t)nVersion));
obj.push_back(Pair("name", name));
obj.push_back(Pair("address", address));
obj.push_back(Pair("paymentaddress", CBitcoinAddress(CTxDestination(address)).ToString()));
obj.push_back(Pair("premine", (int64_t)premine));
obj.push_back(Pair("conversion", (int64_t)conversion));
obj.push_back(Pair("launchfee", (int64_t)launchFee));
@ -378,6 +464,9 @@ bool SetThisChain(UniValue &chainDefinition)
if (ConnectedChains.ThisChain().IsValid())
{
memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
// set all command line parameters into mapArgs from chain definition
vector<string> nodeStrs;
for (auto node : ConnectedChains.ThisChain().nodes)
@ -480,7 +569,7 @@ bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CT
// get the source transaction
uint256 blkHash;
CTransaction thisTx;
if (!myGetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
{
LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
return false;
@ -511,6 +600,9 @@ bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
{
bool retval = false;
LOCK(cs_mergemining);
//printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
auto chainIt = mergeMinedChains.find(chainID);
if (chainIt != mergeMinedChains.end())
{
@ -553,6 +645,7 @@ uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
for (auto id : toRemove)
{
//printf("Pruning chainID: %s\n", id.GetHex().c_str());
RemoveMergedBlock(id);
}
}
@ -570,26 +663,13 @@ bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
auto it = mergeMinedChains.find(cID);
if (it != mergeMinedChains.end())
{
// remove and reinsert target, replace data
target.SetCompact(it->second.block.nBits);
for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
{
// make sure we don't just match by target
if (removeRange.first->second->GetChainID() == cID)
{
mergeMinedTargets.erase(removeRange.first);
break;
}
}
it->second = blkData;
target.SetCompact(blkData.block.nBits);
mergeMinedTargets.insert(make_pair(target, &it->second));
}
else
{
target.SetCompact(blkData.block.nBits);
mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
RemoveMergedBlock(cID); // remove it if already there
}
target.SetCompact(blkData.block.nBits);
//printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
dirty = true;
}
return true;
@ -624,9 +704,10 @@ CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
{
printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
//printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
{
LOCK(cs_mergemining);
qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
}
sem_submitthread.post();
@ -683,11 +764,15 @@ vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
submissionFound = true;
}
//else // not an error condition. code was here for debugging
//else // not an error condition. code is here for debugging
//{
// printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
//}
}
//else // not an error condition. code is here for debugging
//{
// printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
//}
}
// if this header matched no block, discard and move to the next, otherwise, we'll drop through
@ -767,6 +852,7 @@ uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
{
// get the native PBaaS header for each chain and put it into the
// header we are given
// it must have itself in as a PBaaS header
uint160 cid = chain.second.GetChainID();
if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
{
@ -785,9 +871,15 @@ uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
}
}
}
else
{
LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
}
}
dirty = false;
}
return target.GetCompact();
}
@ -859,35 +951,69 @@ void CConnectedChains::SubmissionThread()
{
if (IsVerusActive())
{
// blocks get discarded after no refresh for 5 minutes by default
// blocks get discarded after no refresh for 5 minutes by default, probably should be more often
//printf("SubmissionThread: pruning\n");
ConnectedChains.PruneOldChains(GetAdjustedTime() - 300);
bool submit = false;
{
LOCK(cs_mergemining);
if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
{
qualifiedHeaders.clear();
}
submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
//printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
}
if (submit)
{
//printf("SubmissionThread: calling submit qualified blocks\n");
SubmitQualifiedBlocks();
}
else
{
//printf("SubmissionThread: waiting on sem\n");
sem_submitthread.wait();
}
}
else
{
UniValue result;
// if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
CheckVerusPBaaSAvailable();
for (int i = 0; i < 10; i++)
// check to see if we have recently earned a block with an earned notarization that qualifies for
// submitting an accepted notarization
if (earnedNotarizationHeight)
{
boost::this_thread::interruption_point();
sleep(1);
CBlock blk;
int32_t txIndex = -1, height;
{
LOCK(cs_mergemining);
if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
{
blk = earnedNotarizationBlock;
earnedNotarizationBlock = CBlock();
txIndex = earnedNotarizationIndex;
height = earnedNotarizationHeight;
earnedNotarizationHeight = 0;
}
}
if (txIndex != -1)
{
printf("SubmissionThread: testing notarization\n");
uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
if (!txId.IsNull())
{
printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
}
}
}
sleep(1);
}
boost::this_thread::interruption_point();
}
}
@ -902,8 +1028,29 @@ void CConnectedChains::SubmissionThreadStub()
ConnectedChains.SubmissionThread();
}
void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
{
// called after winning a block that contains an earned notarization
// the earned notarization and its height are queued for processing by the submission thread
// when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
// kept
LOCK(cs_mergemining);
// we only care about the last
earnedNotarizationHeight = height;
earnedNotarizationBlock = blk;
earnedNotarizationIndex = txIndex;
}
bool IsChainDefinitionInput(const CScript &scriptSig)
{
uint32_t ecode;
return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
}
bool IsServiceRewardInput(const CScript &scriptSig)
{
// this is an output check, and is incorrect. need to change to input
uint32_t ecode;
return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_SERVICEREWARD;
}

185
src/pbaas/pbaas.h

@ -21,6 +21,7 @@
#include "script/script.h"
#include "amount.h"
#include "pbaas/crosschainrpc.h"
#include "mmr.h"
#include <boost/algorithm/string.hpp>
@ -53,33 +54,34 @@ static const uint32_t PBAAS_VERSION = 1;
static const uint32_t PBAAS_VERSION_INVALID = 0;
static const uint32_t PBAAS_NODESPERNOTARIZATION = 2; // number of nodes to reference in each notarization
static const int64_t PBAAS_MINNOTARIZATIONOUTPUT = 10000; // enough for one fee worth to finalization and notarization thread
static const int32_t PBAAS_MINSTARTBLOCKDELTA = 100; // minimum number of blocks to wait for starting a chain after definition
static const int64_t PBAAS_MAXNOTARIZATIONINPUTS = 50; // max inputs on a notarization tx
static const int32_t PBAAS_MINSTARTBLOCKDELTA = 10; // minimum number of blocks to wait for starting a chain after definition
static const int32_t PBAAS_MAXPRIORBLOCKS = 16; // maximum prior block commitments to include in prior blocks chain object
enum CURRENCY_OPTIONS {
CURRENCY_FUNGIBLE = 1,
CURRENCY_FRACTIONAL = 2
};
// we wil uncomment service types as they are implemented
// commented service types are here as guidance and reminders
enum PBAAS_SERVICE_TYPES {
SERVICE_INVALID = 0,
SERVICE_MINING = 1,
SERVICE_STAKING = 2,
SERVICE_NOTARIZATION = 3,
SERVICE_NODE = 4,
SERVICE_ELECTRUM = 5,
SERVICE_LAST = 5
SERVICE_NOTARIZATION = 1,
//SERVICE_NODE = 2,
//SERVICE_ELECTRUM = 3,
SERVICE_LAST = 1
};
// these are object types that can be stored and recognized in an opret array
enum CHAIN_OBJECT_TYPES
{
CHAINOBJ_INVALID = 0,
CHAINOBJ_HEADER = 1,
CHAINOBJ_TRANSACTION = 2,
CHAINOBJ_PROOF = 3,
CHAINOBJ_OPRETPROOF = 4,
CHAINOBJ_HEADER_REF = 5,
CHAINOBJ_TRANSACTION_REF = 6,
CHAINOBJ_OPRET_REF = 7
CHAINOBJ_HEADER = 1, // serialized full block header
CHAINOBJ_TRANSACTION = 2, // serialized transaction, sometimes without an opret, which will be reconstructed
CHAINOBJ_PROOF = 3, // merkle proof of preceding block or transaction
CHAINOBJ_HEADER_REF = 4, // equivalent to header, but only includes non-canonical data, assuming merge mine reconstruction
CHAINOBJ_PRIORBLOCKS = 5 // prior block commitments to ensure recognition of overlapping notarizations
};
template <typename SERIALIZABLE>
@ -140,33 +142,15 @@ public:
}
};
// refers to an object stored in a specific position of a specific opret that stores an object array
class COpRetRef
{
public:
uint256 txid; // transaction with opret
uint16_t index; // index of object in opret
COpRetRef() : txid(), index(0) {}
COpRetRef(uint256 txHash, uint16_t objIndex) : txid(txHash), index(objIndex) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(txid);
READWRITE(index);
}
};
class CHeaderRef
{
public:
uint256 hash;
uint256 hash; // block hash
CPBaaSPreHeader preHeader; // non-canonical pre-header data of source chain
CHeaderRef() : hash() {}
CHeaderRef(uint256 &rHash) : hash(rHash) {}
CHeaderRef(uint256 &rHash, CPBaaSPreHeader ph) : hash(rHash), preHeader(ph) {}
CHeaderRef(const CBlockHeader &bh) : hash(bh.GetHash()), preHeader(bh) {}
ADD_SERIALIZE_METHODS;
@ -174,23 +158,26 @@ public:
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(hash);
READWRITE(preHeader);
}
uint256 GetHash() { return hash; }
};
class CTransactionRef
class CPriorBlocksCommitment
{
public:
uint256 hash;
std::vector<uint256> priorBlocks; // prior block commitments, which are node hashes that include merkle root, block hash, and compact power
CTransactionRef() : hash() {}
CTransactionRef(uint256 &rHash) : hash(rHash) {}
CPriorBlocksCommitment() : priorBlocks() {}
CPriorBlocksCommitment(std::vector<uint256> priors) : priorBlocks(priors) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(hash);
READWRITE(priorBlocks);
}
};
@ -219,7 +206,7 @@ public:
CChainObject() : CBaseChainObject() {}
CChainObject(uint16_t objType, SERIALIZABLE &rObject) : CBaseChainObject(objType)
CChainObject(uint16_t objType, const SERIALIZABLE &rObject) : CBaseChainObject(objType)
{
object = rObject;
}
@ -249,9 +236,9 @@ uint256 GetChainObjectHash(const CBaseChainObject &bo);
// returns a pointer to a base chain object, which can be cast to the
// object type indicated in its objType member
template <typename OStream>
CBaseChainObject *RehydrateChainObject(OStream s)
CBaseChainObject *RehydrateChainObject(OStream &s)
{
uint8_t objType;
uint16_t objType;
s >> objType;
@ -260,8 +247,7 @@ CBaseChainObject *RehydrateChainObject(OStream s)
CChainObject<CTransaction> *pNewTx;
CChainObject<CMerkleBranch> *pNewProof;
CChainObject<CHeaderRef> *pNewHeaderRef;
CChainObject<CTransactionRef> *pNewTxRef;
CChainObject<COpRetRef> *pNewOpRetRef;
CChainObject<CPriorBlocksCommitment> *pPriors;
CBaseChainObject *retPtr;
};
@ -301,20 +287,12 @@ CBaseChainObject *RehydrateChainObject(OStream s)
pNewHeaderRef->objectType = objType;
}
break;
case CHAINOBJ_TRANSACTION_REF:
pNewTxRef = new CChainObject<CTransactionRef>();
if (pNewTxRef)
{
s >> pNewTxRef->object;
pNewTxRef->objectType = objType;
}
break;
case CHAINOBJ_OPRET_REF:
pNewOpRetRef = new CChainObject<COpRetRef>();
if (pNewOpRetRef)
case CHAINOBJ_PRIORBLOCKS:
pPriors = new CChainObject<CPriorBlocksCommitment>();
if (pPriors)
{
s >> pNewOpRetRef->object;
pNewOpRetRef->objectType = objType;
s >> pPriors->object;
pPriors->objectType = objType;
}
break;
}
@ -324,47 +302,52 @@ CBaseChainObject *RehydrateChainObject(OStream s)
// returns a pointer to a base chain object, which can be cast to the
// object type indicated in its objType member
template <typename OStream>
bool DehydrateChainObject(OStream s, const CBaseChainObject *pobj)
bool DehydrateChainObject(OStream &s, const CBaseChainObject *pobj)
{
s << pobj->objectType;
switch(pobj->objectType)
{
case CHAINOBJ_HEADER:
{
s << *(CChainObject<CBlockHeader> *)pobj;
return true;
}
case CHAINOBJ_TRANSACTION:
{
s << *(CChainObject<CTransaction> *)pobj;
return true;
}
case CHAINOBJ_PROOF:
{
s << *(CChainObject<CMerkleBranch> *)pobj;
return true;
}
case CHAINOBJ_HEADER_REF:
{
s << *(CChainObject<CHeaderRef> *)pobj;
return true;
}
case CHAINOBJ_TRANSACTION_REF:
s << *(CChainObject<CTransactionRef> *)pobj;
case CHAINOBJ_PRIORBLOCKS:
{
s << *(CChainObject<CPriorBlocksCommitment> *)pobj;
return true;
}
}
return false;
}
int8_t ObjTypeCode(COpRetProof &obj);
int8_t ObjTypeCode(const CBlockHeader &obj);
int8_t ObjTypeCode(CBlockHeader &obj);
int8_t ObjTypeCode(const CMerkleBranch &obj);
int8_t ObjTypeCode(CMerkleBranch &obj);
int8_t ObjTypeCode(const CTransaction &obj);
int8_t ObjTypeCode(CTransaction &obj);
int8_t ObjTypeCode(const CHeaderRef &obj);
int8_t ObjTypeCode(CHeaderRef &obj);
int8_t ObjTypeCode(CTransactionRef &obj);
int8_t ObjTypeCode(COpRetRef &obj);
int8_t ObjTypeCode(const CPriorBlocksCommitment &obj);
// this creates an opret script that stores a specific chain object
template <typename SERIALIZABLE>
@ -382,19 +365,25 @@ std::vector<unsigned char> StoreChainObject(const SERIALIZABLE &obj)
// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs);
void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora);
std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript);
class CNodeData
{
public:
std::string networkAddress;
std::string paymentAddress;
CKeyID paymentAddress;
CNodeData() {}
CNodeData(UniValue &);
CNodeData(std::string netAddr, uint160 paymentKeyID) : networkAddress(netAddr), paymentAddress(paymentKeyID) {}
CNodeData(std::string netAddr, std::string paymentAddr) :
networkAddress(netAddr), paymentAddress(paymentAddr) {}
networkAddress(netAddr)
{
CBitcoinAddress ba(paymentAddr);
ba.GetKeyID(paymentAddress);
}
ADD_SERIALIZE_METHODS;
@ -417,7 +406,7 @@ public:
uint32_t nVersion; // version of this chain definition data structure to allow for extensions (not daemon version)
std::string name; // chain name, maximum 64 characters
std::string address; // non-purchased/converted premine and fee recipient address
CKeyID address; // non-purchased/converted premine and fee recipient address
int64_t premine; // initial supply that is distributed to the premine output address, but not purchased
int64_t conversion; // factor / 100000000 for conversion of VRSC to coin on launch
int64_t launchFee; // ratio of satoshis to send from contribution to convertible to fee address
@ -453,7 +442,6 @@ public:
int32_t BillingPeriod, int64_t NotaryReward, std::vector<CNodeData> &Nodes) :
nVersion(PBAAS_VERSION),
name(Name),
address(Address),
premine(Premine),
conversion(Conversion),
launchFee(LaunchFee),
@ -474,6 +462,8 @@ public:
Name.resize(KOMODO_ASSETCHAIN_MAXLEN - 1);
}
name = Name;
CBitcoinAddress ba(Address);
ba.GetKeyID(address);
}
ADD_SERIALIZE_METHODS;
@ -565,23 +555,20 @@ public:
class CServiceReward
{
public:
uint32_t nVersion; // version of this chain definition data structure to allow for extensions (not daemon version)
uint16_t serviceType; // type of service
CAmount nPerReward; // amount to pay per reward
int32_t billingPeriod; // this is used to identify to which billing period of a chain, this reward applies
CServiceReward() : serviceType(SERVICE_INVALID) {}
CServiceReward() : nVersion(PBAAS_VERSION_INVALID), serviceType(SERVICE_INVALID) {}
CServiceReward(PBAAS_SERVICE_TYPES ServiceType, CAmount PerReward)
{
serviceType = ServiceType;
nPerReward = PerReward;
}
CServiceReward(PBAAS_SERVICE_TYPES ServiceType, int32_t period) : nVersion(PBAAS_VERSION), serviceType(ServiceType), billingPeriod(period) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(serviceType);
READWRITE(VARINT(nPerReward));
READWRITE(billingPeriod);
}
CServiceReward(const std::vector<unsigned char> &asVector)
@ -589,6 +576,26 @@ public:
FromVector(asVector, *this);
}
CServiceReward(const UniValue &obj) : nVersion(PBAAS_VERSION)
{
serviceType = uni_get_str(find_value(obj, "servicetype")) == "notarization" ? SERVICE_NOTARIZATION : SERVICE_INVALID;
billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
if (!billingPeriod)
{
serviceType = SERVICE_INVALID;
}
}
CServiceReward(const CTransaction &tx, bool validate = false);
UniValue ToUniValue() const
{
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("servicetype", serviceType == SERVICE_NOTARIZATION ? "notarization" : "unknown"));
obj.push_back(Pair("billingperiod", billingPeriod));
return obj;
}
std::vector<unsigned char> AsVector()
{
return ::AsVector(*this);
@ -863,6 +870,10 @@ public:
CPBaaSChainDefinition thisChain;
int32_t earnedNotarizationHeight; // zero or the height of one or more potential submissions
CBlock earnedNotarizationBlock;
int32_t earnedNotarizationIndex; // index of earned notarization in block
bool dirty;
bool lastSubmissionFailed; // if we submit a failed block, make another
std::map<arith_uint256, CBlockHeader> qualifiedHeaders;
@ -870,7 +881,7 @@ public:
CCriticalSection cs_mergemining;
CSemaphore sem_submitthread;
CConnectedChains() : sem_submitthread(0), dirty(0), lastSubmissionFailed(0) {}
CConnectedChains() : sem_submitthread(0), earnedNotarizationHeight(0), dirty(0), lastSubmissionFailed(0) {}
arith_uint256 LowestTarget()
{
@ -889,6 +900,7 @@ public:
std::vector<std::pair<std::string, UniValue>> SubmitQualifiedBlocks();
bool QueueNewBlockHeader(CBlockHeader &bh);
void QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height);
bool AddMergedBlock(CPBaaSMergeMinedChainData &blkData);
bool RemoveMergedBlock(uint160 chainID);
@ -966,6 +978,7 @@ bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransact
// used to validate a specific service reward based on the spending transaction
bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool IsServiceRewardInput(const CScript &scriptSig);
// used as a proxy token output for a reserve currency on its fractional reserve chain
bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);

13
src/pow.cpp

@ -159,7 +159,18 @@ unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const
// Check we have enough blocks
if (!pindexFirst)
return bnLimit.GetCompact();
{
if (!_IsVerusActive() && ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
{
// startup 16 times harder on PBaaS chains
bnLimit = bnLimit >> 4;
return bnLimit.GetCompact();
}
else
{
return bnLimit.GetCompact();
}
}
// Keep t reasonable in case strange solvetimes occurred.
if (t < N * k / 3)

6
src/primitives/block.cpp

@ -73,7 +73,7 @@ arith_uint256 GetCompactPower(const uint256 &nNonce, uint32_t nBits, int32_t ver
}
}
CPBaaSPreHeader::CPBaaSPreHeader(CBlockHeader &bh)
CPBaaSPreHeader::CPBaaSPreHeader(const CBlockHeader &bh)
{
hashPrevBlock = bh.hashPrevBlock;
hashMerkleRoot = bh.hashMerkleRoot;
@ -89,7 +89,7 @@ CMMRPowerNode CBlockHeader::GetMMRNode() const
uint256 preHash = Hash(BEGIN(hashMerkleRoot), END(hashMerkleRoot), BEGIN(blockHash), END(blockHash));
uint256 power = ArithToUint256(GetCompactPower(nNonce, nBits, nVersion));
return CMMRPowerNode(Hash(BEGIN(hashMerkleRoot), END(hashMerkleRoot), BEGIN(power), END(power)), power);
return CMMRPowerNode(Hash(BEGIN(preHash), END(preHash), BEGIN(power), END(power)), power);
}
void CBlockHeader::AddMerkleProofBridge(CMerkleBranch &branch) const
@ -101,7 +101,7 @@ void CBlockHeader::AddMerkleProofBridge(CMerkleBranch &branch) const
void CBlockHeader::AddBlockProofBridge(CMerkleBranch &branch) const
{
// we need to add the block hash on the right
// we need to add the merkle root on the left
branch.branch.push_back(hashMerkleRoot);
branch.nIndex = branch.nIndex << 1 + 1;
}

42
src/primitives/block.h

@ -20,35 +20,6 @@ class CMMRPowerNode;
class CMerkleBranch;
class CBlockHeader;
/*
class CPBaaSPreHeader
{
public:
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint256 hashFinalSaplingRoot;
uint256 nNonce;
uint32_t nBits;
CPBaaSPreHeader() : nBits(0) {}
CPBaaSPreHeader(const uint256 &prevBlock, const uint256 &merkleRoot, const uint256 &finalSaplingRoot,const uint256 &nonce, uint32_t compactTarget) :
hashPrevBlock(prevBlock), hashMerkleRoot(merkleRoot), hashFinalSaplingRoot(finalSaplingRoot), nNonce(nonce), nBits(compactTarget) {}
CPBaaSPreHeader(CBlockHeader &bh);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(hashFinalSaplingRoot);
READWRITE(nNonce);
READWRITE(nBits);
}
};
*/
/** 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
* requirements. When they solve the proof-of-work, they broadcast the block
@ -139,6 +110,11 @@ public:
CVerusSolutionVector(nSolution).ResizeExtraData(newSize);
}
uint32_t ExtraDataLen()
{
return CVerusSolutionVector(nSolution).ExtraDataLen();
}
// returns -1 on failure, upon failure, pbbh is undefined and likely corrupted
int32_t GetPBaaSHeader(CPBaaSBlockHeader &pbh, const uint160 &cID) const;
@ -156,6 +132,14 @@ public:
return false;
}
// returns false on failure to read data
int32_t NumPBaaSHeaders() const
{
// search in the solution for this header index and return it if found
CPBaaSSolutionDescriptor descr = CConstVerusSolutionVector::GetDescriptor(nSolution);
return descr.numPBaaSHeaders;
}
// this can save a new header into an empty space or update an existing header
bool SavePBaaSHeader(CPBaaSBlockHeader &pbh, uint32_t idx)
{

4
src/primitives/solutiondata.h

@ -75,7 +75,7 @@ public:
CPBaaSPreHeader(const uint256 &prevBlock, const uint256 &merkleRoot, const uint256 &finalSaplingRoot,const uint256 &nonce, uint32_t compactTarget) :
hashPrevBlock(prevBlock), hashMerkleRoot(merkleRoot), hashFinalSaplingRoot(finalSaplingRoot), nNonce(nonce), nBits(compactTarget) {}
CPBaaSPreHeader(CBlockHeader &bh);
CPBaaSPreHeader(const CBlockHeader &bh);
ADD_SERIALIZE_METHODS;
@ -502,7 +502,7 @@ class CVerusSolutionVector
auto descr = Descriptor();
descr.extraDataSize = len;
SetDescriptor(descr);
std::memcpy(&(vch.data()[4]), pbegin, len);
std::memcpy(ExtraDataPtr(), pbegin, len);
return true;
}
};

6
src/primitives/transaction.cpp

@ -384,14 +384,12 @@ int64_t CTransaction::UnlockTime(uint32_t voutNum) const
{
if (vout.size() > voutNum + 1 && vout[voutNum].scriptPubKey.IsPayToScriptHash())
{
uint32_t voutNext = voutNum + 1;
std::vector<uint8_t> opretData;
uint160 scriptID = uint160(std::vector<unsigned char>(vout[voutNum].scriptPubKey.begin() + 2, vout[voutNum].scriptPubKey.begin() + 22));
CScript::const_iterator it = vout[voutNext].scriptPubKey.begin() + 1;
CScript::const_iterator it = vout.back().scriptPubKey.begin() + 1;
opcodetype op;
if (vout[voutNext].scriptPubKey.GetOp2(it, op, &opretData))
if (vout.back().scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData.data()[0] == OPRETTYPE_TIMELOCK)
{

5
src/rpc/mining.cpp

@ -25,6 +25,7 @@
#include "wallet/wallet.h"
#endif
#include "pbaas/pbaas.h"
#include "pbaas/notarization.h"
#include <stdint.h>
@ -579,8 +580,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
std::string strMode = "template";
UniValue lpval = NullUniValue;
// TODO: Re-enable coinbasevalue once a specification has been written
bool coinbasetxn = true;
if (params.size() > 0)
{
const UniValue& oparam = params[0].get_obj();
@ -884,7 +887,7 @@ UniValue submitblock(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}
printf("Received block submission for %s\nhash %s\n", ASSETCHAINS_SYMBOL, block.GetHash().GetHex().c_str());
printf("Received block submission for %s\nhash: %s\n", ASSETCHAINS_SYMBOL, block.GetHash().GetHex().c_str());
uint256 hash = block.GetHash();
bool fBlockPresent = false;

4
src/rpc/misc.cpp

@ -132,10 +132,10 @@ UniValue getinfo(const UniValue& params, bool fHelp)
#endif
//fprintf(stderr,"after wallet %u\n",(uint32_t)time(NULL));
obj.push_back(Pair("blocks", (int)chainActive.Height()));
if ( (longestchain= KOMODO_LONGESTCHAIN) != 0 && chainActive.Height() > longestchain )
if ( (longestchain = KOMODO_LONGESTCHAIN) == 0 && chainActive.Height() > longestchain )
longestchain = chainActive.Height();
//fprintf(stderr,"after longestchain %u\n",(uint32_t)time(NULL));
obj.push_back(Pair("longestchain", longestchain));
obj.push_back(Pair("longestchain", longestchain));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
if ( chainActive.LastTip() != 0 )
obj.push_back(Pair("tiptime", (int)chainActive.LastTip()->nTime));

785
src/rpc/pbaasrpc.cpp

File diff suppressed because it is too large

2
src/rpc/pbaasrpc.h

@ -16,7 +16,7 @@
#include <univalue.h>
bool GetChainDefinition(std::string &name, CPBaaSChainDefinition &chainDef);
bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData &notarizationData);
bool GetNotarizationData(uint160 chainID, uint32_t ecode, CChainNotarizationData &notarizationData, std::vector<std::pair<CTransaction, uint256>> *optionalTxOut = NULL);
UniValue getchaindefinition(const UniValue& params, bool fHelp);
UniValue getnotarizationdata(const UniValue& params, bool fHelp);

2
src/script/script.cpp

@ -387,7 +387,7 @@ bool CScript::IsInstantSpend() const
switch(ecode)
{
case EVAL_EARNEDNOTARIZATION:
case EVAL_FINALIZENOTARIZATION:
//case EVAL_FINALIZENOTARIZATION:
return true;
}
}

8
src/script/script_ext.cpp

@ -94,14 +94,14 @@ bool CScriptExt::ExtractVoutDestination(const CTransaction& tx, int32_t voutNum,
CScriptExt spk = tx.vout[voutNum].scriptPubKey;
// if this is a timelocked transaction, get the destination behind the time lock
if (tx.IsCoinBase() && tx.vout.size() == 2 && voutNum == 0 &&
if (tx.IsCoinBase() && voutNum == 0 &&
spk.IsPayToScriptHash(&scriptHash) &&
tx.vout[1].scriptPubKey.IsOpReturn())
tx.vout.back().scriptPubKey.IsOpReturn())
{
opcodetype op;
std::vector<uint8_t> opretData = std::vector<uint8_t>();
CScript::const_iterator it = tx.vout[1].scriptPubKey.begin() + 1;
if (tx.vout[1].scriptPubKey.GetOp2(it, op, &opretData))
CScript::const_iterator it = tx.vout.back().scriptPubKey.begin() + 1;
if (tx.vout.back().scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData[0] == OPRETTYPE_TIMELOCK)
{

66
src/txmempool.cpp

@ -131,37 +131,41 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC
std::vector<CMempoolAddressDeltaKey> inserted;
uint256 txhash = tx.GetHash();
for (unsigned int j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(input);
if (prevout.scriptPubKey.IsPayToScriptHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34);
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToCryptoCondition()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin(), prevout.scriptPubKey.end());
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
} }
if (!tx.IsCoinBase())
{
for (unsigned int j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(input);
if (prevout.scriptPubKey.IsPayToScriptHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22);
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34);
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
}
else if (prevout.scriptPubKey.IsPayToCryptoCondition()) {
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin(), prevout.scriptPubKey.end());
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
mapAddress.insert(make_pair(key, delta));
inserted.push_back(key);
} }
}
for (unsigned int k = 0; k < tx.vout.size(); k++) {
const CTxOut &out = tx.vout[k];

1
src/wallet/asyncrpcoperation_sendmany.cpp

@ -1052,6 +1052,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
}
// By default we ignore coinbase outputs
// TODO: audit use of fAcceptCoinbase to ensure that when coinbase is not required to be shielded that this is skipped
bool isCoinbase = out.tx->IsCoinBase();
if (isCoinbase && fAcceptCoinbase==false) {
continue;

6
src/wallet/asyncrpcoperation_shieldcoinbase.cpp

@ -270,6 +270,9 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c
// Add transparent inputs
for (auto t : m_op->inputs_) {
m_op->builder_.SetLockTime((uint32_t)(chainActive.Height()));
m_op->builder_.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount, 0xfffffffe);
/*
if ((uint64_t)t.amount >= ASSETCHAINS_TIMELOCKGTE)
{
m_op->builder_.SetLockTime((uint32_t)(chainActive.Height()));
@ -279,11 +282,14 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c
{
m_op->builder_.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount);
}
*/
}
// Send all value to the target z-addr
m_op->builder_.SendChangeTo(zaddr, ovk);
// Build the transaction
auto maybe_tx = m_op->builder_.Build();
if (!maybe_tx) {

25
src/wallet/wallet.cpp

@ -1335,7 +1335,7 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult,
pBlock->nNonce.SetPOSTarget(bnTarget, pBlock->nVersion);
target.SetCompact(bnTarget);
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false);
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, !Params().GetConsensus().fCoinbaseMustBeProtected);
if (pastBlockIndex = komodo_chainactive(nHeight - 100))
{
@ -1348,13 +1348,18 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult,
if (txout.fSpendable && (UintToArith256(txout.tx->GetVerusPOSHash(&(pBlock->nNonce), txout.i, nHeight, pastHash)) <= target) && (txout.nDepth >= VERUS_MIN_STAKEAGE))
{
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
int32_t txSize = GetSerializeSize(s, *(CTransaction *)txout.tx);
printf("Serialized size of transaction %lu\n", GetSerializeSize(s, *(CTransaction *)txout.tx));
//printf("Serialized size of transaction %s is %lu\n", txout.tx->GetHash().GetHex().c_str(), txSize);
if (txSize > MAX_TX_SIZE_FOR_STAKING)
{
LogPrintf("Transaction %s is too large to stake. Serialized size == %lu\n", txout.tx->GetHash().GetHex().c_str(), txSize);
}
if ((!pwinner || UintToArith256(curNonce) > UintToArith256(pBlock->nNonce)) &&
if (txSize <= MAX_TX_SIZE_FOR_STAKING &&
(!pwinner || UintToArith256(curNonce) > UintToArith256(pBlock->nNonce)) &&
(Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH)) &&
!cheatList.IsUTXOInList(COutPoint(txout.tx->GetHash(), txout.i), nHeight <= 100 ? 1 : nHeight-100) &&
GetSerializeSize(s, *(CTransaction *)txout.tx) <= MAX_TX_SIZE_FOR_STAKING)
!cheatList.IsUTXOInList(COutPoint(txout.tx->GetHash(), txout.i), nHeight <= 100 ? 1 : nHeight-100))
{
//printf("Found PoS block\nnNonce: %s\n", pBlock->nNonce.GetHex().c_str());
pwinner = &txout;
@ -2297,16 +2302,16 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum)
return ret;
}
}
else if (tx.vout.size() > (voutNext = voutNum + 1) &&
tx.vout[voutNext].scriptPubKey.size() > 7 &&
tx.vout[voutNext].scriptPubKey[0] == OP_RETURN)
else if (tx.vout.size() > (voutNum + 1) &&
tx.vout.back().scriptPubKey.size() > 7 &&
tx.vout.back().scriptPubKey[0] == OP_RETURN)
{
// get the opret script from next vout, verify that the front is CLTV and hash matches
// if so, remove it and use the solver
opcodetype op;
std::vector<uint8_t> opretData;
CScript::const_iterator it = tx.vout[voutNext].scriptPubKey.begin() + 1;
if (tx.vout[voutNext].scriptPubKey.GetOp2(it, op, &opretData))
CScript::const_iterator it = tx.vout.back().scriptPubKey.begin() + 1;
if (tx.vout.back().scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData[0] == OPRETTYPE_TIMELOCK)
{

Loading…
Cancel
Save