Implementation of Overwinter transaction format ZIP 202.

This commit is contained in:
Simon
2018-02-15 22:19:36 -08:00
parent d527116d46
commit 072099d788
25 changed files with 1125 additions and 66 deletions

View File

@@ -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;
}