Auto merge of #4542 - nuttycom:reorder_checks, r=nuttycom
Organize transaction version checks. Some minor cleanup for ContextualCheckTransaction, in service of making the checks here easier to reason about.
This commit is contained in:
commit
d3acf75745
|
@ -311,7 +311,7 @@ TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
|
|||
|
||||
{
|
||||
SCOPED_TRACE("BlockOverwinterRulesRejectSproutTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwintered-flag-not-set");
|
||||
}
|
||||
|
||||
// Make it a Sapling transaction
|
||||
|
@ -321,7 +321,7 @@ TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
|
|||
|
||||
{
|
||||
SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-tx-overwinter-version-too-high");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) {
|
|||
|
||||
{
|
||||
SCOPED_TRACE("BlockSaplingRulesRejectSproutTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwintered-flag-not-set");
|
||||
}
|
||||
|
||||
// Make it an Overwinter transaction
|
||||
|
@ -368,7 +368,7 @@ TEST_F(ContextualCheckBlockTest, BlockBlossomRulesRejectOtherTx) {
|
|||
|
||||
{
|
||||
SCOPED_TRACE("BlockBlossomRulesRejectSproutTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwintered-flag-not-set");
|
||||
}
|
||||
|
||||
// Make it an Overwinter transaction
|
||||
|
|
|
@ -71,7 +71,7 @@ CMutableTransaction GetValidTransaction(uint32_t consensusBranchId=SPROUT_BRANCH
|
|||
mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
||||
mtx.vin[1].prevout.n = 0;
|
||||
mtx.vout.resize(2);
|
||||
// mtx.vout[0].scriptPubKey =
|
||||
// mtx.vout[0].scriptPubKey =
|
||||
mtx.vout[0].nValue = 0;
|
||||
mtx.vout[1].nValue = 0;
|
||||
mtx.vJoinSplit.resize(2);
|
||||
|
@ -947,7 +947,7 @@ TEST(ChecktransactionTests, OverwinterFlagNotSet) {
|
|||
|
||||
CTransaction tx(mtx);
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwintered-flag-not-set", false)).Times(1);
|
||||
ContextualCheckTransaction(tx, state, Params(), 1, true);
|
||||
|
||||
// Revert to default
|
||||
|
|
|
@ -167,7 +167,7 @@ TEST(Mempool, SproutV3TxWhenOverwinterActive) {
|
|||
|
||||
LOCK(cs_main);
|
||||
EXPECT_FALSE(AcceptToMemoryPool(pool, state1, tx1, false, &missingInputs));
|
||||
EXPECT_EQ(state1.GetRejectReason(), "tx-overwinter-flag-not-set");
|
||||
EXPECT_EQ(state1.GetRejectReason(), "tx-overwintered-flag-not-set");
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
|
|
182
src/main.cpp
182
src/main.cpp
|
@ -761,7 +761,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
|||
|
||||
/**
|
||||
* Check a transaction contextually against a set of consensus rules valid at a given block height.
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
* 1. AcceptToMemoryPool calls CheckTransaction and this function.
|
||||
* 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction)
|
||||
|
@ -792,29 +792,73 @@ bool ContextualCheckTransaction(
|
|||
|
||||
bool overwinterActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER);
|
||||
bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING);
|
||||
bool isSprout = !overwinterActive;
|
||||
bool beforeOverwinter = !overwinterActive;
|
||||
bool heartwoodActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD);
|
||||
bool canopyActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY);
|
||||
|
||||
// If Sprout rules apply, reject transactions which are intended for Overwinter and beyond
|
||||
if (isSprout && tx.fOverwintered) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("ContextualCheckTransaction(): overwinter is not active yet"),
|
||||
REJECT_INVALID, "tx-overwinter-not-active");
|
||||
assert(!saplingActive || overwinterActive); // Sapling cannot be active unless Overwinter is
|
||||
assert(!heartwoodActive || saplingActive); // Heartwood cannot be active unless Sapling is
|
||||
assert(!canopyActive || heartwoodActive); // Canopy cannot be active unless Heartwood is
|
||||
|
||||
// Rules that apply only to Sprout
|
||||
if (beforeOverwinter) {
|
||||
// Reject transactions which are intended for Overwinter and beyond
|
||||
if (tx.fOverwintered) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("ContextualCheckTransaction(): overwinter is not active yet"),
|
||||
REJECT_INVALID, "tx-overwinter-not-active");
|
||||
}
|
||||
}
|
||||
|
||||
if (saplingActive) {
|
||||
// Reject transactions with valid version but missing overwintered flag
|
||||
if (tx.nVersion >= SAPLING_MIN_TX_VERSION && !tx.fOverwintered) {
|
||||
// Rules that apply to Overwinter and later:
|
||||
if (overwinterActive) {
|
||||
// Reject transactions intended for Sprout
|
||||
if (!tx.fOverwintered) {
|
||||
return state.DoS(
|
||||
dosLevelConstricting,
|
||||
error("ContextualCheckTransaction(): overwintered flag must be set"),
|
||||
error("ContextualCheckTransaction: fOverwintered flag must be set when Overwinter is active"),
|
||||
REJECT_INVALID, "tx-overwintered-flag-not-set");
|
||||
}
|
||||
|
||||
// The condition `if (tx.nVersion < OVERWINTER_MIN_TX_VERSION)`
|
||||
// that would otherwise fire here is already performed in the
|
||||
// noncontextual checks in CheckTransactionWithoutProofVerification
|
||||
|
||||
// Check that all transactions are unexpired
|
||||
if (IsExpiredTx(tx, nHeight)) {
|
||||
// Don't increase banscore if the transaction only just expired
|
||||
int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? dosLevelConstricting : 0;
|
||||
return state.DoS(
|
||||
expiredDosLevel,
|
||||
error("ContextualCheckTransaction(): transaction is expired. Resending when caught up with the blockchain, or manually setting the zcashd txexpirydelta parameter may help."),
|
||||
REJECT_INVALID, "tx-overwinter-expired");
|
||||
}
|
||||
|
||||
// Rules that became inactive after Sapling activation.
|
||||
if (!saplingActive) {
|
||||
// Reject transactions with invalid version
|
||||
if (tx.nVersion > OVERWINTER_MAX_TX_VERSION ) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): overwinter version too high"),
|
||||
REJECT_INVALID, "bad-tx-overwinter-version-too-high");
|
||||
}
|
||||
|
||||
// Reject transactions with non-Overwinter version group ID
|
||||
if (tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): invalid Overwinter tx version"),
|
||||
REJECT_INVALID, "bad-overwinter-tx-version-group-id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules that apply to Sapling and later:
|
||||
if (saplingActive) {
|
||||
// Reject transactions with non-Sapling version group ID
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) {
|
||||
if (tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): invalid Sapling tx version"),
|
||||
|
@ -822,7 +866,7 @@ bool ContextualCheckTransaction(
|
|||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
if (tx.fOverwintered && tx.nVersion < SAPLING_MIN_TX_VERSION ) {
|
||||
if (tx.nVersion < SAPLING_MIN_TX_VERSION) {
|
||||
return state.DoS(
|
||||
dosLevelConstricting,
|
||||
error("CheckTransaction(): Sapling version too low"),
|
||||
|
@ -830,62 +874,18 @@ bool ContextualCheckTransaction(
|
|||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
if (tx.fOverwintered && tx.nVersion > SAPLING_MAX_TX_VERSION ) {
|
||||
if (tx.nVersion > SAPLING_MAX_TX_VERSION) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): Sapling version too high"),
|
||||
REJECT_INVALID, "bad-tx-sapling-version-too-high");
|
||||
}
|
||||
} else if (overwinterActive) {
|
||||
// Reject transactions with valid version but missing overwinter flag
|
||||
if (tx.nVersion >= OVERWINTER_MIN_TX_VERSION && !tx.fOverwintered) {
|
||||
return state.DoS(
|
||||
dosLevelConstricting,
|
||||
error("ContextualCheckTransaction(): overwinter flag must be set"),
|
||||
REJECT_INVALID, "tx-overwinter-flag-not-set");
|
||||
}
|
||||
} else {
|
||||
// Rules that apply generally before Sapling. These were
|
||||
// previously noncontextual checks that became contextual
|
||||
// after Sapling activation.
|
||||
|
||||
// Reject transactions with non-Overwinter version group ID
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): invalid Overwinter tx version"),
|
||||
REJECT_INVALID, "bad-overwinter-tx-version-group-id");
|
||||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) {
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): overwinter version too high"),
|
||||
REJECT_INVALID, "bad-tx-overwinter-version-too-high");
|
||||
}
|
||||
}
|
||||
|
||||
// Rules that apply to Overwinter or later:
|
||||
if (overwinterActive) {
|
||||
// Reject transactions intended for Sprout
|
||||
if (!tx.fOverwintered) {
|
||||
return state.DoS(
|
||||
dosLevelConstricting,
|
||||
error("ContextualCheckTransaction: overwinter is active"),
|
||||
REJECT_INVALID, "tx-overwinter-active");
|
||||
}
|
||||
|
||||
// Check that all transactions are unexpired
|
||||
if (IsExpiredTx(tx, nHeight)) {
|
||||
// Don't increase banscore if the transaction only just expired
|
||||
int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? dosLevelConstricting : 0;
|
||||
return state.DoS(
|
||||
expiredDosLevel,
|
||||
error("ContextualCheckTransaction(): transaction is expired. Resending when caught up with the blockchain, or manually setting the zcashd txexpirydelta parameter may help."),
|
||||
REJECT_INVALID, "tx-overwinter-expired");
|
||||
}
|
||||
}
|
||||
|
||||
// Rules that apply before Sapling:
|
||||
if (!saplingActive) {
|
||||
// Size limits
|
||||
// Reject transactions that exceed pre-sapling size limits
|
||||
BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE > MAX_TX_SIZE_BEFORE_SAPLING); // sanity
|
||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_BEFORE_SAPLING)
|
||||
return state.DoS(
|
||||
|
@ -894,31 +894,16 @@ bool ContextualCheckTransaction(
|
|||
REJECT_INVALID, "bad-txns-oversize");
|
||||
}
|
||||
|
||||
// Rules that apply before Heartwood:
|
||||
if (!heartwoodActive) {
|
||||
if (tx.IsCoinBase()) {
|
||||
// A coinbase transaction cannot have output descriptions
|
||||
if (tx.vShieldedOutput.size() > 0)
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): coinbase has output descriptions"),
|
||||
REJECT_INVALID, "bad-cb-has-output-description");
|
||||
}
|
||||
}
|
||||
|
||||
// From Canopy onward, coinbase transaction must include outputs corresponding to the
|
||||
// ZIP 207 consensus funding streams active at the current block height. To avoid
|
||||
// double-decrypting, we detect any shielded funding streams during the Heartwood
|
||||
// consensus check. If Canopy is not yet active, fundingStreamElements will be empty.
|
||||
std::set<Consensus::FundingStreamElement> fundingStreamElements;
|
||||
if (canopyActive) {
|
||||
fundingStreamElements = Consensus::GetActiveFundingStreamElements(
|
||||
nHeight,
|
||||
GetBlockSubsidy(nHeight, chainparams.GetConsensus()),
|
||||
chainparams.GetConsensus());
|
||||
}
|
||||
std::set<Consensus::FundingStreamElement> fundingStreamElements = Consensus::GetActiveFundingStreamElements(
|
||||
nHeight,
|
||||
GetBlockSubsidy(nHeight, chainparams.GetConsensus()),
|
||||
chainparams.GetConsensus());
|
||||
|
||||
// Rules that apply to Heartwood or later:
|
||||
// Rules that apply to Heartwood and later:
|
||||
if (heartwoodActive) {
|
||||
if (tx.IsCoinBase()) {
|
||||
// All Sapling outputs in coinbase transactions MUST have valid note commitments
|
||||
|
@ -932,8 +917,7 @@ bool ContextualCheckTransaction(
|
|||
return state.DoS(
|
||||
DOS_LEVEL_BLOCK,
|
||||
error("CheckTransaction(): coinbase output description has invalid outCiphertext"),
|
||||
REJECT_INVALID,
|
||||
"bad-cb-output-desc-invalid-outct");
|
||||
REJECT_INVALID, "bad-cb-output-desc-invalid-outct");
|
||||
}
|
||||
|
||||
// SaplingNotePlaintext::decrypt() checks note commitment validity.
|
||||
|
@ -950,8 +934,7 @@ bool ContextualCheckTransaction(
|
|||
return state.DoS(
|
||||
DOS_LEVEL_BLOCK,
|
||||
error("CheckTransaction(): coinbase output description has invalid encCiphertext"),
|
||||
REJECT_INVALID,
|
||||
"bad-cb-output-desc-invalid-encct");
|
||||
REJECT_INVALID, "bad-cb-output-desc-invalid-encct");
|
||||
}
|
||||
|
||||
// ZIP 207: detect shielded funding stream elements
|
||||
|
@ -980,6 +963,19 @@ bool ContextualCheckTransaction(
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Rules that apply generally before Heartwood. These were
|
||||
// previously noncontextual checks that became contextual
|
||||
// after Heartwood activation.
|
||||
|
||||
if (tx.IsCoinBase()) {
|
||||
// A coinbase transaction cannot have output descriptions
|
||||
if (tx.vShieldedOutput.size() > 0)
|
||||
return state.DoS(
|
||||
dosLevelPotentiallyRelaxing,
|
||||
error("CheckTransaction(): coinbase has output descriptions"),
|
||||
REJECT_INVALID, "bad-cb-has-output-description");
|
||||
}
|
||||
}
|
||||
|
||||
// Rules that apply to Canopy or later:
|
||||
|
@ -1008,6 +1004,10 @@ bool ContextualCheckTransaction(
|
|||
REJECT_INVALID, "cb-funding-stream-missing");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Rules that apply generally before Canopy. These were
|
||||
// previously noncontextual checks that became contextual
|
||||
// after Canopy activation.
|
||||
}
|
||||
|
||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
|
||||
|
@ -1663,7 +1663,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||
{
|
||||
// We lock to prevent other threads from accessing the mempool between adding and evicting
|
||||
LOCK(pool.cs);
|
||||
|
||||
|
||||
// Store transaction in memory
|
||||
pool.addUnchecked(hash, entry, !IsInitialBlockDownload(Params()));
|
||||
|
||||
|
@ -5048,7 +5048,7 @@ bool LoadBlockIndex()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool InitBlockIndex(const CChainParams& chainparams)
|
||||
bool InitBlockIndex(const CChainParams& chainparams)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
|
@ -6913,7 +6913,7 @@ CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Para
|
|||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nVersion = OVERWINTER_TX_VERSION;
|
||||
}
|
||||
|
||||
|
||||
bool blossomActive = consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM);
|
||||
unsigned int defaultExpiryDelta = blossomActive ? DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA : DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA;
|
||||
mtx.nExpiryHeight = nHeight + (expiryDeltaArg ? expiryDeltaArg.get() : defaultExpiryDelta);
|
||||
|
|
Loading…
Reference in New Issue