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:
Jack Grigg
2018-02-02 01:49:42 +00:00
parent 6514771a44
commit be12669982
26 changed files with 360 additions and 237 deletions

View File

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