|
|
@ -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) |
|
|
|
{ |
|
|
|