Add consensus branch ID parameter to SignatureHash, remove SigVersion parameter
We do not need to be able to calculate multiple SignatureHash versions for a single transaction format; instead, we use the transaction format to determine the SigVersion. The consensus branch ID *does* need to be passed in from the outside, as only the caller knows the context in which the SignatureHash is being calculated (ie. mempool acceptance vs. block validation). JoinSplit signature verification has been moved into ContextualCheckTransaction, where the consensus branch ID can be obtained. The argument to the sign command for zcash-tx has been modified to take a height in addition to the optional sigtype flags.
This commit is contained in:
77
src/main.cpp
77
src/main.cpp
@@ -758,7 +758,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
|
||||
* 2. P2SH scripts with a crazy number of expensive
|
||||
* CHECKSIG/CHECKMULTISIG operations
|
||||
*/
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId)
|
||||
{
|
||||
if (tx.IsCoinBase())
|
||||
return true; // Coinbases don't use vin normally
|
||||
@@ -784,7 +784,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
||||
// IsStandardTx() will have already returned false
|
||||
// and this method isn't called.
|
||||
vector<vector<unsigned char> > stack;
|
||||
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE))
|
||||
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId))
|
||||
return false;
|
||||
|
||||
if (whichType == TX_SCRIPTHASH)
|
||||
@@ -886,6 +886,30 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
||||
// Empty output script.
|
||||
CScript scriptCode;
|
||||
uint256 dataToBeSigned;
|
||||
try {
|
||||
dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
|
||||
} catch (std::logic_error ex) {
|
||||
return state.DoS(100, error("CheckTransaction(): error computing signature hash"),
|
||||
REJECT_INVALID, "error-computing-signature-hash");
|
||||
}
|
||||
|
||||
BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32);
|
||||
|
||||
// We rely on libsodium to check that the signature is canonical.
|
||||
// https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0
|
||||
if (crypto_sign_verify_detached(&tx.joinSplitSig[0],
|
||||
dataToBeSigned.begin(), 32,
|
||||
tx.joinSplitPubKey.begin()
|
||||
) != 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"),
|
||||
REJECT_INVALID, "bad-txns-invalid-joinsplit-signature");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1082,30 +1106,6 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
||||
if (txin.prevout.IsNull())
|
||||
return state.DoS(10, error("CheckTransaction(): prevout is null"),
|
||||
REJECT_INVALID, "bad-txns-prevout-null");
|
||||
|
||||
if (tx.vjoinsplit.size() > 0) {
|
||||
// Empty output script.
|
||||
CScript scriptCode;
|
||||
uint256 dataToBeSigned;
|
||||
try {
|
||||
dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, SIGVERSION_BASE);
|
||||
} catch (std::logic_error ex) {
|
||||
return state.DoS(100, error("CheckTransaction(): error computing signature hash"),
|
||||
REJECT_INVALID, "error-computing-signature-hash");
|
||||
}
|
||||
|
||||
BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32);
|
||||
|
||||
// We rely on libsodium to check that the signature is canonical.
|
||||
// https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0
|
||||
if (crypto_sign_verify_detached(&tx.joinSplitSig[0],
|
||||
dataToBeSigned.begin(), 32,
|
||||
tx.joinSplitPubKey.begin()
|
||||
) != 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"),
|
||||
REJECT_INVALID, "bad-txns-invalid-joinsplit-signature");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1148,6 +1148,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (pfMissingInputs)
|
||||
*pfMissingInputs = false;
|
||||
|
||||
int nextBlockHeight = chainActive.Height() + 1;
|
||||
auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus());
|
||||
|
||||
// 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);
|
||||
@@ -1165,7 +1168,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
|
||||
// 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");
|
||||
}
|
||||
@@ -1260,7 +1262,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
}
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
if (Params().RequireStandard() && !AreInputsStandard(tx, view))
|
||||
if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId))
|
||||
return error("AcceptToMemoryPool: nonstandard transaction input");
|
||||
|
||||
// Check that the transaction doesn't have an excessive number of
|
||||
@@ -1348,7 +1350,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
// Check against previous transactions
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus()))
|
||||
if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
||||
{
|
||||
return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString());
|
||||
}
|
||||
@@ -1362,7 +1364,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
// There is a similar check in CreateNewBlock() to prevent creating
|
||||
// invalid blocks, however allowing such transactions into the mempool
|
||||
// can be exploited as a DoS attack.
|
||||
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus()))
|
||||
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
||||
{
|
||||
return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString());
|
||||
}
|
||||
@@ -1727,7 +1729,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
||||
|
||||
bool CScriptCheck::operator()() {
|
||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
|
||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) {
|
||||
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
|
||||
}
|
||||
return true;
|
||||
@@ -1810,7 +1812,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
||||
}
|
||||
}// namespace Consensus
|
||||
|
||||
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
|
||||
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, const Consensus::Params& consensusParams, uint32_t consensusBranchId, std::vector<CScriptCheck> *pvChecks)
|
||||
{
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
@@ -1835,7 +1837,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
|
||||
assert(coins);
|
||||
|
||||
// Verify signature
|
||||
CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata);
|
||||
CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata);
|
||||
if (pvChecks) {
|
||||
pvChecks->push_back(CScriptCheck());
|
||||
check.swap(pvChecks->back());
|
||||
@@ -1848,7 +1850,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
|
||||
// avoid splitting the network between upgraded and
|
||||
// non-upgraded nodes.
|
||||
CScriptCheck check2(*coins, tx, i,
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata);
|
||||
if (check2())
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
}
|
||||
@@ -2238,6 +2240,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
assert(tree.root() == old_tree_root);
|
||||
}
|
||||
|
||||
// Grab the consensus branch ID for the block's height
|
||||
auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus());
|
||||
|
||||
std::vector<PrecomputedTransactionData> txdata;
|
||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
@@ -2277,7 +2282,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
nFees += view.GetValueIn(tx)-tx.GetValueOut();
|
||||
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL))
|
||||
if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL))
|
||||
return false;
|
||||
control.Add(vChecks);
|
||||
}
|
||||
@@ -3721,7 +3726,7 @@ bool static LoadBlockIndexDB()
|
||||
pindex->nCachedBranchId = pindex->pprev->nCachedBranchId;
|
||||
}
|
||||
} else {
|
||||
pindex->nCachedBranchId = NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId;
|
||||
pindex->nCachedBranchId = SPROUT_BRANCH_ID;
|
||||
}
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL))
|
||||
setBlockIndexCandidates.insert(pindex);
|
||||
|
||||
Reference in New Issue
Block a user