mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-09 11:27:28 -03:00
Merge f9bc6ba48d
into 66aa6a47bd
This commit is contained in:
commit
a82ced19d6
7 changed files with 199 additions and 20 deletions
|
@ -1781,6 +1781,35 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
|
|||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckTaprootCommitment(
|
||||
const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const
|
||||
{
|
||||
assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
|
||||
assert(program.size() >= uint256::size());
|
||||
//! The internal pubkey (x-only, so no Y coordinate parity).
|
||||
const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)};
|
||||
//! The output pubkey (taken from the scriptPubKey).
|
||||
const XOnlyPubKey q{program};
|
||||
// Compute the Merkle root from the leaf and the provided path.
|
||||
const uint256 merkle_root = ComputeTaprootMerkleRoot(control, tapleaf_hash);
|
||||
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
|
||||
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckWitnessScriptHash(
|
||||
Span<const unsigned char> program,
|
||||
const CScript& exec_script) const
|
||||
{
|
||||
assert(program.size() >= uint256::size());
|
||||
uint256 hash_exec_script;
|
||||
CSHA256().Write(exec_script.data(), exec_script.size()).Finalize(hash_exec_script.begin());
|
||||
return memcmp(hash_exec_script.begin(), program.data(), uint256::size()) == 0;
|
||||
}
|
||||
|
||||
// explicit instantiation
|
||||
template class GenericTransactionSignatureChecker<CTransaction>;
|
||||
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
@ -1856,20 +1885,6 @@ uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint25
|
|||
return k;
|
||||
}
|
||||
|
||||
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const uint256& tapleaf_hash)
|
||||
{
|
||||
assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE);
|
||||
assert(program.size() >= uint256::size());
|
||||
//! The internal pubkey (x-only, so no Y coordinate parity).
|
||||
const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)};
|
||||
//! The output pubkey (taken from the scriptPubKey).
|
||||
const XOnlyPubKey q{program};
|
||||
// Compute the Merkle root from the leaf and the provided path.
|
||||
const uint256 merkle_root = ComputeTaprootMerkleRoot(control, tapleaf_hash);
|
||||
// Verify that the output pubkey matches the tweaked internal pubkey, after correcting for parity.
|
||||
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
||||
}
|
||||
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||
{
|
||||
CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
|
||||
|
@ -1884,9 +1899,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
}
|
||||
const valtype& script_bytes = SpanPopBack(stack);
|
||||
exec_script = CScript(script_bytes.begin(), script_bytes.end());
|
||||
uint256 hash_exec_script;
|
||||
CSHA256().Write(exec_script.data(), exec_script.size()).Finalize(hash_exec_script.begin());
|
||||
if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
|
||||
if (!checker.CheckWitnessScriptHash(program, exec_script)) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
|
||||
}
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
|
||||
|
@ -1927,7 +1940,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
|
||||
}
|
||||
execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script);
|
||||
if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) {
|
||||
if (!checker.CheckTaprootCommitment(control, program, execdata.m_tapleaf_hash)) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
|
||||
}
|
||||
execdata.m_tapleaf_hash_init = true;
|
||||
|
|
|
@ -265,6 +265,18 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ~BaseSignatureChecker() = default;
|
||||
};
|
||||
|
||||
|
@ -301,6 +313,11 @@ public:
|
|||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override;
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override;
|
||||
};
|
||||
|
||||
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
|
||||
|
@ -332,6 +349,18 @@ public:
|
|||
{
|
||||
return m_checker.CheckSequence(nSequence);
|
||||
}
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return m_checker.CheckTaprootCommitment(control, program, tapleaf_hash);
|
||||
}
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return m_checker.CheckWitnessScriptHash(program, exec_script);
|
||||
}
|
||||
};
|
||||
|
||||
/** Compute the BIP341 tapleaf hash from leaf version & script. */
|
||||
|
|
|
@ -704,6 +704,19 @@ public:
|
|||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const override { return sig.size() != 0; }
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override { return true; }
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override { return true; }
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -299,6 +299,18 @@ const struct CheckerContext: BaseSignatureChecker {
|
|||
}
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override { return nLockTime.GetInt64() & 1; }
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override { return nSequence.GetInt64() & 1; }
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} CHECKER_CTX;
|
||||
|
||||
//! Context to check for duplicates when instancing a Node.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <consensus/amount.h>
|
||||
#include <crypto/siphash.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <serialize.h>
|
||||
|
@ -15,7 +16,75 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
FUZZ_TARGET(script_flags)
|
||||
namespace {
|
||||
|
||||
inline uint64_t HashSig(Span<const unsigned char> sig)
|
||||
{
|
||||
CSipHasher hasher{0xdead, 0xbeef};
|
||||
hasher.Write(sig);
|
||||
return hasher.Finalize();
|
||||
}
|
||||
// Reduce a CScriptNum to one bit
|
||||
inline bool HashScriptNum(const CScriptNum& num)
|
||||
{
|
||||
CSipHasher hasher{0xdead, 0xbeef};
|
||||
hasher.Write(num.getvch());
|
||||
return hasher.Finalize() & 1;
|
||||
}
|
||||
|
||||
class FuzzedSignatureChecker : public BaseSignatureChecker
|
||||
{
|
||||
public:
|
||||
FuzzedSignatureChecker(const CTransaction* tx, unsigned int in,
|
||||
const CAmount& amount, const PrecomputedTransactionData& tx_data,
|
||||
MissingDataBehavior mdb) {}
|
||||
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pub_key,
|
||||
const CScript& script_code, SigVersion sig_version) const override
|
||||
{
|
||||
return !sig.empty() && (HashSig(sig) & 1);
|
||||
}
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pub_key,
|
||||
SigVersion sig_version, ScriptExecutionData& exec_data,
|
||||
ScriptError* script_error = nullptr) const override
|
||||
{
|
||||
uint64_t fuzz_hash{HashSig(sig)};
|
||||
bool sig_ok = fuzz_hash & 1;
|
||||
if (!sig_ok && script_error) {
|
||||
constexpr std::array<ScriptError, 3> schnorr_errs = {
|
||||
SCRIPT_ERR_SCHNORR_SIG,
|
||||
SCRIPT_ERR_SCHNORR_SIG_SIZE,
|
||||
SCRIPT_ERR_SCHNORR_SIG_HASHTYPE};
|
||||
*script_error = schnorr_errs[(fuzz_hash >> 1) % schnorr_errs.size()];
|
||||
}
|
||||
|
||||
return sig_ok;
|
||||
}
|
||||
bool CheckLockTime(const CScriptNum& lock_time) const override
|
||||
{
|
||||
return HashScriptNum(lock_time);
|
||||
}
|
||||
bool CheckSequence(const CScriptNum& sequence) const override
|
||||
{
|
||||
return HashScriptNum(sequence);
|
||||
}
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return !program.empty() && (program[0] & 1);
|
||||
}
|
||||
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return !program.empty() && (program[0] & 1);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SigChecker>
|
||||
void CheckScriptFlags(FuzzBufferType buffer)
|
||||
{
|
||||
if (buffer.size() > 100'000) return;
|
||||
DataStream ds{buffer};
|
||||
|
@ -45,7 +114,7 @@ FUZZ_TARGET(script_flags)
|
|||
|
||||
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};
|
||||
const SigChecker 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);
|
||||
|
@ -69,3 +138,21 @@ FUZZ_TARGET(script_flags)
|
|||
return;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
/**
|
||||
* Both of the following harnesses test that all script verification flags only
|
||||
* tighten the interpreter rules (i.e. they represent soft-forks).
|
||||
*/
|
||||
|
||||
FUZZ_TARGET(script_flags)
|
||||
{
|
||||
CheckScriptFlags<TransactionSignatureChecker>(buffer);
|
||||
}
|
||||
|
||||
// Signature validation is mocked out through FuzzedSignatureChecker
|
||||
FUZZ_TARGET(script_flags_mocked)
|
||||
{
|
||||
CheckScriptFlags<FuzzedSignatureChecker>(buffer);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,18 @@ public:
|
|||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
||||
virtual ~FuzzedSignatureChecker() = default;
|
||||
};
|
||||
} // namespace
|
||||
|
|
|
@ -289,6 +289,19 @@ public:
|
|||
// Delegate to Satisfier.
|
||||
return ctx.CheckOlder(sequence.GetInt64());
|
||||
}
|
||||
|
||||
bool CheckTaprootCommitment(const std::vector<unsigned char>& control,
|
||||
const std::vector<unsigned char>& program,
|
||||
const uint256& tapleaf_hash) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckWitnessScriptHash(Span<const unsigned char> program,
|
||||
const CScript& exec_script) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using Fragment = miniscript::Fragment;
|
||||
|
|
Loading…
Reference in a new issue