mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-06 10:18:44 -05:00
![fanquake](/assets/img/avatar_default.png)
725d7ae049
Use PrecomputedTransactionData in signet check (Pieter Wuille)497718b467
Treat amount<0 also as missing data for P2WPKH/P2WSH (Pieter Wuille)3820090bd6
Make all SignatureChecker explicit about missing data (Pieter Wuille)b77b0cc507
Add MissingDataBehavior and make TransactionSignatureChecker handle it (Pieter Wuille) Pull request description: Currently we have 2 levels of potentially-missing data in the transaction signature hashes: * P2WPKH/P2WSH hashes need the spent amount * P2TR hashes need all spent outputs (amount + scriptPubKey) Missing amounts are treated as -1 (thus leading to unexpected signature failures), while missing outputs in P2TR validation cause assertion failure. This is hard to extend for signing support, and also quite ugly in general. In this PR, an explicit configuration option to {Mutable,}TransactionSignatureChecker is added (MissingDataBehavior enum class) to either select ASSERT_FAIL or FAIL. Validation code passes ASSERT_FAIL (as at validation time all data should always be passed, and anything else is a serious bug in the code), while signing code uses FAIL. The existence of the ASSERT_FAIL option is really just an abundance of caution. Always using FAIL should be just fine, but if there were for some reason a code path in consensus code was introduced that misses certain data, I think we prefer as assertion failure over silently introducing a consensus change. Potentially useful follow-ups (not for this PR, in my preference): * Having an explicit script validation error code for missing data. * Having a MissingDataBehavior::SUCCEED option as well, for use in script/sign.cpp DataFromTransaction (if a signature is present in a witness, and we don't have enough data to fully validate it, we should probably treat it as valid and not touch it). ACKs for top commit: sanket1729: reACK725d7ae049
Sjors: ACK725d7ae049
achow101: re-ACK725d7ae049
benthecarman: ACK725d7ae049
fjahr: Code review ACK725d7ae049
Tree-SHA512: d67dc51bae9ca7ef6eb9acccefd682529f397830f77d74cd305500a081ef55aede0e9fa380648c3a8dd4857aa7eeb1ab54fe808979d79db0784ac94ceb31b657
74 lines
2.4 KiB
C++
74 lines
2.4 KiB
C++
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <pubkey.h>
|
|
#include <script/interpreter.h>
|
|
#include <streams.h>
|
|
#include <test/util/script.h>
|
|
#include <version.h>
|
|
|
|
#include <test/fuzz/fuzz.h>
|
|
|
|
void initialize_script_flags()
|
|
{
|
|
static const ECCVerifyHandle verify_handle;
|
|
}
|
|
|
|
FUZZ_TARGET_INIT(script_flags, initialize_script_flags)
|
|
{
|
|
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
|
|
try {
|
|
int nVersion;
|
|
ds >> nVersion;
|
|
ds.SetVersion(nVersion);
|
|
} catch (const std::ios_base::failure&) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const CTransaction tx(deserialize, ds);
|
|
|
|
unsigned int verify_flags;
|
|
ds >> verify_flags;
|
|
|
|
if (!IsValidFlagCombination(verify_flags)) return;
|
|
|
|
unsigned int fuzzed_flags;
|
|
ds >> fuzzed_flags;
|
|
|
|
std::vector<CTxOut> spent_outputs;
|
|
for (unsigned i = 0; i < tx.vin.size(); ++i) {
|
|
CTxOut prevout;
|
|
ds >> prevout;
|
|
spent_outputs.push_back(prevout);
|
|
}
|
|
PrecomputedTransactionData txdata;
|
|
txdata.Init(tx, std::move(spent_outputs));
|
|
|
|
for (unsigned i = 0; i < tx.vin.size(); ++i) {
|
|
const CTxOut& prevout = txdata.m_spent_outputs.at(i);
|
|
const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata, MissingDataBehavior::ASSERT_FAIL};
|
|
|
|
ScriptError serror;
|
|
const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
|
|
assert(ret == (serror == SCRIPT_ERR_OK));
|
|
|
|
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result
|
|
if (ret) {
|
|
verify_flags &= ~fuzzed_flags;
|
|
} else {
|
|
verify_flags |= fuzzed_flags;
|
|
}
|
|
if (!IsValidFlagCombination(verify_flags)) return;
|
|
|
|
ScriptError serror_fuzzed;
|
|
const bool ret_fuzzed = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror_fuzzed);
|
|
assert(ret_fuzzed == (serror_fuzzed == SCRIPT_ERR_OK));
|
|
|
|
assert(ret_fuzzed == ret);
|
|
}
|
|
} catch (const std::ios_base::failure&) {
|
|
return;
|
|
}
|
|
}
|