Implementation of Overwinter transaction format ZIP 202.
This commit is contained in:
141
src/main.cpp
141
src/main.cpp
@@ -641,16 +641,22 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRE
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsStandardTx(const CTransaction& tx, string& reason)
|
||||
bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight)
|
||||
{
|
||||
if (tx.nVersion > CTransaction::MAX_CURRENT_VERSION || tx.nVersion < CTransaction::MIN_CURRENT_VERSION) {
|
||||
reason = "version";
|
||||
return false;
|
||||
bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
|
||||
|
||||
if (isOverwinter) {
|
||||
// Overwinter standard rules apply
|
||||
if (tx.nVersion > CTransaction::OVERWINTER_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::OVERWINTER_MIN_CURRENT_VERSION) {
|
||||
reason = "overwinter-version";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Sprout standard rules apply
|
||||
if (tx.nVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION || tx.nVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION) {
|
||||
reason = "version";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
@@ -840,6 +846,50 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* and ContextualCheckBlock (which calls this function).
|
||||
*/
|
||||
bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, const int nHeight, const int dosLevel)
|
||||
{
|
||||
bool isOverwinter = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
|
||||
bool isSprout = !isOverwinter;
|
||||
|
||||
// If Sprout rules apply, reject transactions which are intended for Overwinter and beyond
|
||||
if (isSprout && tx.fOverwintered) {
|
||||
return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter is not active yet"),
|
||||
REJECT_INVALID, "tx-overwinter-not-active");
|
||||
}
|
||||
|
||||
// If Overwinter rules apply:
|
||||
if (isOverwinter) {
|
||||
// Reject transactions with valid version but missing overwinter flag
|
||||
if (tx.nVersion >= OVERWINTER_MIN_TX_VERSION && !tx.fOverwintered) {
|
||||
return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter flag must be set"),
|
||||
REJECT_INVALID, "tx-overwinter-flag-not-set");
|
||||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) {
|
||||
return state.DoS(100, error("CheckTransaction(): overwinter version too high"),
|
||||
REJECT_INVALID, "bad-tx-overwinter-version-too-high");
|
||||
}
|
||||
|
||||
// Reject transactions intended for Sprout
|
||||
if (!tx.fOverwintered) {
|
||||
return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"),
|
||||
REJECT_INVALID, "tx-overwinter-active");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
libzcash::ProofVerifier& verifier)
|
||||
{
|
||||
@@ -866,11 +916,46 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
||||
{
|
||||
// Basic checks that don't depend on any context
|
||||
|
||||
// Check transaction version
|
||||
if (tx.nVersion < MIN_TX_VERSION) {
|
||||
/**
|
||||
* Previously:
|
||||
* 1. The consensus rule below was:
|
||||
* if (tx.nVersion < SPROUT_MIN_TX_VERSION) { ... }
|
||||
* which checked if tx.nVersion fell within the range:
|
||||
* INT32_MIN <= tx.nVersion < SPROUT_MIN_TX_VERSION
|
||||
* 2. The parser allowed tx.nVersion to be negative
|
||||
*
|
||||
* Now:
|
||||
* 1. The consensus rule checks to see if tx.Version falls within the range:
|
||||
* 0 <= tx.nVersion < SPROUT_MIN_TX_VERSION
|
||||
* 2. The previous consensus rule checked for negative values within the range:
|
||||
* INT32_MIN <= tx.nVersion < 0
|
||||
* This is unnecessary for Overwinter transactions since the parser now
|
||||
* interprets the sign bit as fOverwintered, so tx.nVersion is always >=0,
|
||||
* and when Overwinter is not active ContextualCheckTransaction rejects
|
||||
* transactions with fOverwintered set. When fOverwintered is set,
|
||||
* this function and ContextualCheckTransaction will together check to
|
||||
* ensure tx.nVersion avoids the following ranges:
|
||||
* 0 <= tx.nVersion < OVERWINTER_MIN_TX_VERSION
|
||||
* OVERWINTER_MAX_TX_VERSION < tx.nVersion <= INT32_MAX
|
||||
*/
|
||||
if (!tx.fOverwintered && tx.nVersion < SPROUT_MIN_TX_VERSION) {
|
||||
return state.DoS(100, error("CheckTransaction(): version too low"),
|
||||
REJECT_INVALID, "bad-txns-version-too-low");
|
||||
}
|
||||
else if (tx.fOverwintered) {
|
||||
if (tx.nVersion < OVERWINTER_MIN_TX_VERSION) {
|
||||
return state.DoS(100, error("CheckTransaction(): overwinter version too low"),
|
||||
REJECT_INVALID, "bad-tx-overwinter-version-too-low");
|
||||
}
|
||||
if (tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) {
|
||||
return state.DoS(100, error("CheckTransaction(): unknown tx version group id"),
|
||||
REJECT_INVALID, "bad-tx-version-group-id");
|
||||
}
|
||||
if (tx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) {
|
||||
return state.DoS(100, error("CheckTransaction(): expiry height is too high"),
|
||||
REJECT_INVALID, "bad-tx-expiry-height-too-high");
|
||||
}
|
||||
}
|
||||
|
||||
// Transactions can contain empty `vin` and `vout` so long as
|
||||
// `vjoinsplit` is non-empty.
|
||||
@@ -1078,6 +1163,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (!CheckTransaction(tx, state, verifier))
|
||||
return error("AcceptToMemoryPool: CheckTransaction failed");
|
||||
|
||||
// DoS level set to 10 to be more forgiving.
|
||||
// Check transaction contextually against the set of consensus rules which apply in the next block to be mined.
|
||||
int nextBlockHeight = chainActive.Height() + 1;
|
||||
if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) {
|
||||
return error("AcceptToMemoryPool: ContextualCheckTransaction failed");
|
||||
}
|
||||
|
||||
// Coinbase is only valid in a block, not as a loose transaction
|
||||
if (tx.IsCoinBase())
|
||||
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),
|
||||
@@ -1085,7 +1177,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
|
||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||
string reason;
|
||||
if (Params().RequireStandard() && !IsStandardTx(tx, reason))
|
||||
if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight))
|
||||
return state.DoS(0,
|
||||
error("AcceptToMemoryPool: nonstandard transaction: %s", reason),
|
||||
REJECT_NONSTANDARD, reason);
|
||||
@@ -3133,6 +3225,12 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
|
||||
|
||||
// Check that all transactions are finalized
|
||||
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
|
||||
|
||||
// Check transaction contextually against consensus rules at block height
|
||||
if (!ContextualCheckTransaction(tx, state, nHeight, 100)) {
|
||||
return false; // Failure reason has been set in validation state object
|
||||
}
|
||||
|
||||
int nLockTimeFlags = 0;
|
||||
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
|
||||
? pindexPrev->GetMedianTimePast()
|
||||
@@ -5735,3 +5833,22 @@ public:
|
||||
mapOrphanTransactionsByPrev.clear();
|
||||
}
|
||||
} instance_of_cmaincleanup;
|
||||
|
||||
|
||||
// Set default values of new CMutableTransaction based on consensus rules at given height.
|
||||
CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
|
||||
bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER);
|
||||
if (isOverwintered) {
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||
mtx.nVersion = 3;
|
||||
// Expiry height is not set. Only fields required for a parser to treat as a valid Overwinter V3 tx.
|
||||
|
||||
// TODO: In future, when moving from Overwinter to Sapling, it will be useful
|
||||
// to set the expiry height to: min(activation_height - 1, default_expiry_height)
|
||||
}
|
||||
return mtx;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user