#include "checkpoints.h"
#include "checkqueue.h"
#include "consensus/validation.h"
+#include "deprecation.h"
#include "init.h"
#include "merkleblock.h"
#include "metrics.h"
#include "utilmoneystr.h"
#include "validationinterface.h"
#include "wallet/asyncrpcoperation_sendmany.h"
+#include "wallet/asyncrpcoperation_shieldcoinbase.h"
#include <sstream>
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
+bool fExperimentalMode = false;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
CTransaction tx;
NodeId fromPeer;
};
-map<uint256, COrphanTx> mapOrphanTransactions;
-map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
-void EraseOrphansFor(NodeId peer);
+map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);;
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);;
+void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
// mapOrphanTransactions
//
-bool AddOrphanTx(const CTransaction& tx, NodeId peer)
+bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return true;
}
-void static EraseOrphanTx(uint256 hash)
+void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
if (it == mapOrphanTransactions.end())
}
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
+unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
/**
* Check transaction inputs to mitigate two
* potential denial-of-service attacks:
- *
+ *
* 1. scriptSigs with extra data stuffed into them,
* not consumed by scriptPubKey (or P2SH script)
* 2. P2SH scripts with a crazy number of expensive
if (pfMissingInputs)
*pfMissingInputs = false;
+ // Node operator can choose to reject tx by number of transparent inputs
+ static_assert(std::numeric_limits<size_t>::max() >= std::numeric_limits<int64_t>::max(), "size_t too small");
+ size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0);
+ if (limit > 0) {
+ 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;
+ }
+ }
+
auto verifier = libzcash::ProofVerifier::Strict();
if (!CheckTransaction(tx, state, verifier))
return error("AcceptToMemoryPool: CheckTransaction failed");
return true;
}
-bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
+int GetSpendHeight(const CCoinsViewCache& inputs)
{
- if (!tx.IsCoinBase())
- {
- if (pvChecks)
- pvChecks->reserve(tx.vin.size());
+ LOCK(cs_main);
+ CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
+ return pindexPrev->nHeight + 1;
+}
+namespace Consensus {
+bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams)
+{
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx))
assert(coins);
if (coins->IsCoinBase()) {
+ // Ensure that coinbases are matured
+ if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
+ return state.Invalid(
+ error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
+ REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
+ }
+
// Ensure that coinbases cannot be spent to transparent outputs
// Disabled on regtest
if (fCoinbaseEnforcedProtectionEnabled &&
if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs(): nFees out of range"),
REJECT_INVALID, "bad-txns-fee-outofrange");
+ return true;
+}
+}// namespace Consensus
+
+bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
+{
+ if (!tx.IsCoinBase())
+ {
+ if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
+ return false;
+ }
+
+ if (pvChecks)
+ pvChecks->reserve(tx.vin.size());
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
return true;
}
-bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
-{
- if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) {
- return false;
- }
-
- if (!tx.IsCoinBase())
- {
- // While checking, GetBestBlock() refers to the parent block.
- // This is also true for mempool checks.
- CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
- int nSpendHeight = pindexPrev->nHeight + 1;
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- const COutPoint &prevout = tx.vin[i].prevout;
- const CCoins *coins = inputs.AccessCoins(prevout.hash);
- // Assertion is okay because NonContextualCheckInputs ensures the inputs
- // are available.
- assert(coins);
-
- // If prev is coinbase, check that it's matured
- if (coins->IsCoinBase()) {
- if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
- return state.Invalid(
- error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
- REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
- }
- }
- }
- }
-
- return true;
-}
-
namespace {
bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
- bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
+ bool fExpensiveChecks = true;
+ if (fCheckpointsEnabled) {
+ CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints());
+ if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) {
+ // This block is an ancestor of a checkpoint: disable script checks
+ fExpensiveChecks = false;
+ }
+ }
+
auto verifier = libzcash::ProofVerifier::Strict();
auto disabledVerifier = libzcash::ProofVerifier::Disabled();
// Before the genesis block, there was an empty tree
ZCIncrementalMerkleTree tree;
pindex->hashAnchor = tree.root();
+ // The genesis block contained no JoinSplits
+ pindex->hashAnchorEnd = pindex->hashAnchor;
}
return true;
}
REJECT_INVALID, "bad-txns-BIP30");
}
- unsigned int flags = SCRIPT_VERIFY_P2SH;
+ unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
- // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks,
- // when 75% of the network has upgraded:
- if (block.nVersion >= 3) {
- flags |= SCRIPT_VERIFY_DERSIG;
- }
-
- // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4
- // blocks, when 75% of the network has upgraded:
- if (block.nVersion >= 4) {
- flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
- }
+ // DERSIG (BIP66) is also always enforced, but does not have a flag.
CBlockUndo blockundo;
}
view.PushAnchor(tree);
+ if (!fJustCheck) {
+ pindex->hashAnchorEnd = tree.root();
+ }
blockundo.old_tree_root = old_tree_root;
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION);
if (nUpgraded > 100/2)
{
- // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
+ // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete; upgrade required!");
CAlert::Notify(strMiscWarning, true);
fWarned = true;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 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.
*/
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
- CInv inv(MSG_BLOCK, pindexNew->GetBlockHash());
bool rv = ConnectBlock(*pblock, state, pindexNew, view);
GetMainSignals().BlockChecked(*pblock, state);
if (!rv) {
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
- mapBlockSource.erase(inv.hash);
+ mapBlockSource.erase(pindexNew->GetBlockHash());
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true);
+ EnforceNodeDeprecation(pindexNew->nHeight);
+
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
// Notify external listeners about the new tip.
- GetMainSignals().UpdatedBlockTip(hashNewTip);
+ GetMainSignals().UpdatedBlockTip(pindexNewTip);
uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
return state.Invalid(error("%s: block's timestamp is too early", __func__),
REJECT_INVALID, "time-too-old");
- if(fCheckpointsEnabled)
+ if (fCheckpointsEnabled)
{
- // Check that the block chain matches the known block chain up to a checkpoint
- if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash))
- return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),
- REJECT_CHECKPOINT, "checkpoint mismatch");
-
// Don't accept any forks from the main chain prior to last checkpoint
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
}
}
- // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
- // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
- // Since MIN_BLOCK_VERSION = 4 all blocks with nHeight > 0 should satisfy this.
- // This rule is not applied to the genesis block, which didn't include the height
- // in the coinbase.
+ // Enforce BIP 34 rule that the coinbase starts with serialized block height.
+ // In Zcash this has been enforced since launch, except that the genesis
+ // block didn't include the height in the coinbase (see Zcash protocol spec
+ // section '6.8 Bitcoin Improvement Proposals').
if (nHeight > 0)
{
CScript expect = CScript() << nHeight;
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
+ // Fill in-memory data
+ BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
+ {
+ CBlockIndex* pindex = item.second;
+ // - This relationship will always be true even if pprev has multiple
+ // children, because hashAnchor is technically a property of pprev,
+ // not its children.
+ // - This will miss chain tips; we handle the best tip below, and other
+ // tips will be handled by ConnectTip during a re-org.
+ if (pindex->pprev) {
+ pindex->pprev->hashAnchorEnd = pindex->hashAnchor;
+ }
+ }
+
// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
return true;
chainActive.SetTip(it->second);
+ // Set hashAnchorEnd for the end of best chain
+ it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor();
PruneBlockIndexCandidates();
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip()));
+ EnforceNodeDeprecation(chainActive.Height(), true);
+
return true;
}
// CAlert
//
-string GetWarnings(string strFor)
+std::string GetWarnings(const std::string& strFor)
{
int nPriority = 0;
string strStatusBar;
//
-bool static AlreadyHave(const CInv& inv)
+bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
switch (inv.type)
{
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
{
+ LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
} else if (IsPeerAddrLocalGood(pfrom)) {
addr.SetIP(pfrom->addrLocal);
+ LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
}
}