mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
commit
e765fc93ea
45 changed files with 3034 additions and 139 deletions
|
|
@ -12,7 +12,7 @@
|
|||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
|
|
|||
|
|
@ -81,11 +81,10 @@ else
|
|||
fi
|
||||
|
||||
if [ ! -d ${DIR_QA_ASSETS} ]; then
|
||||
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
|
||||
DOCKER_EXEC git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
|
||||
fi
|
||||
DOCKER_EXEC git clone --depth=1 https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
|
||||
fi
|
||||
export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/
|
||||
export DIR_UNIT_TEST_DATA=${DIR_QA_ASSETS}/unit_test_data/
|
||||
|
||||
DOCKER_EXEC mkdir -p "${BASE_SCRATCH_DIR}/sanitizer-output/"
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ fi
|
|||
|
||||
if [ "$RUN_UNIT_TESTS" = "true" ]; then
|
||||
BEGIN_FOLD unit-tests
|
||||
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
|
||||
DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
|
||||
END_FOLD
|
||||
fi
|
||||
|
||||
if [ "$RUN_UNIT_TESTS_SEQUENTIAL" = "true" ]; then
|
||||
BEGIN_FOLD unit-tests-seq
|
||||
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
|
||||
DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
|
||||
END_FOLD
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1727,7 +1727,7 @@ if test x$need_bundled_univalue = xyes; then
|
|||
AC_CONFIG_SUBDIRS([src/univalue])
|
||||
fi
|
||||
|
||||
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-experimental --enable-module-whitelist --enable-module-rangeproof --enable-module-generator --enable-module-surjectionproof --enable-module-ecdh"
|
||||
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental --enable-module-whitelist --enable-module-rangeproof --enable-module-generator --enable-module-surjectionproof --enable-module-ecdh"
|
||||
AC_CONFIG_SUBDIRS([src/secp256k1])
|
||||
|
||||
AC_OUTPUT
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ FUZZ_TARGETS = \
|
|||
test/fuzz/script_deserialize \
|
||||
test/fuzz/script_flags \
|
||||
test/fuzz/script_interpreter \
|
||||
test/fuzz/script_assets_test_minimizer \
|
||||
test/fuzz/script_ops \
|
||||
test/fuzz/script_sigcache \
|
||||
test/fuzz/script_sign \
|
||||
|
|
@ -1090,6 +1091,12 @@ test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
|
|||
test_fuzz_script_interpreter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
|
||||
test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
|
||||
|
||||
test_fuzz_script_assets_test_minimizer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
test_fuzz_script_assets_test_minimizer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
test_fuzz_script_assets_test_minimizer_LDADD = $(FUZZ_SUITE_LD_COMMON)
|
||||
test_fuzz_script_assets_test_minimizer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||
test_fuzz_script_assets_test_minimizer_SOURCES = test/fuzz/script_assets_test_minimizer.cpp
|
||||
|
||||
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@ public:
|
|||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// Deployment of Taproot (BIPs 340-342)
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000e1ab5ec9348e9f4b8eb8154");
|
||||
|
||||
|
|
@ -239,6 +244,11 @@ public:
|
|||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// Deployment of Taproot (BIPs 340-342)
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001495c1d5a01e2af8a23");
|
||||
|
||||
|
|
@ -451,6 +461,9 @@ public:
|
|||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||
|
||||
// The best chain should have at least this much work.
|
||||
consensus.nMinimumChainWork = uint256S("0x00");
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Consensus {
|
|||
enum DeploymentPos
|
||||
{
|
||||
DEPLOYMENT_TESTDUMMY,
|
||||
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
|
||||
DEPLOYMENT_DYNA_FED, // Deployment of dynamic federation
|
||||
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
|
||||
MAX_VERSION_BITS_DEPLOYMENTS
|
||||
|
|
|
|||
10
src/hash.cpp
10
src/hash.cpp
|
|
@ -6,6 +6,7 @@
|
|||
#include <crypto/common.h>
|
||||
#include <crypto/hmac_sha512.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
inline uint32_t ROTL32(uint32_t x, int8_t r)
|
||||
{
|
||||
|
|
@ -84,3 +85,12 @@ uint256 SHA256Uint256(const uint256& input)
|
|||
CSHA256().Write(input.begin(), 32).Finalize(result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
CHashWriter TaggedHash(const std::string& tag)
|
||||
{
|
||||
CHashWriter writer(SER_GETHASH, 0);
|
||||
uint256 taghash;
|
||||
CSHA256().Write((const unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
|
||||
writer << taghash << taghash;
|
||||
return writer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <uint256.h>
|
||||
#include <version.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef uint256 ChainCode;
|
||||
|
|
@ -202,4 +203,12 @@ unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vData
|
|||
|
||||
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
|
||||
|
||||
/** Return a CHashWriter primed for tagged hashes (as specified in BIP 340).
|
||||
*
|
||||
* The returned object will have SHA256(tag) written to it twice (= 64 bytes).
|
||||
* A tagged hash can be computed by feeding the message into this object, and
|
||||
* then calling CHashWriter::GetSHA256().
|
||||
*/
|
||||
CHashWriter TaggedHash(const std::string& tag);
|
||||
|
||||
#endif // BITCOIN_HASH_H
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <coins.h>
|
||||
#include <primitives/pak.h>
|
||||
#include <script/pegins.h>
|
||||
#include <span.h>
|
||||
#include <chainparams.h> // Peg-out enforcement
|
||||
|
||||
// ELEMENTS:
|
||||
|
|
@ -229,6 +230,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
// get the scriptPubKey corresponding to this input:
|
||||
CScript prevScript = prev.scriptPubKey;
|
||||
|
||||
bool p2sh = false;
|
||||
if (prevScript.IsPayToScriptHash()) {
|
||||
std::vector <std::vector<unsigned char> > stack;
|
||||
// If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig
|
||||
|
|
@ -239,6 +241,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
if (stack.empty())
|
||||
return false;
|
||||
prevScript = CScript(stack.back().begin(), stack.back().end());
|
||||
p2sh = true;
|
||||
}
|
||||
|
||||
int witnessversion = 0;
|
||||
|
|
@ -261,6 +264,40 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check policy limits for Taproot spends:
|
||||
// - MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE limit for stack item size
|
||||
// - No annexes
|
||||
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) {
|
||||
// Missing witness; invalid by consensus rules
|
||||
if (i >= tx.witness.vtxinwit.size()) {
|
||||
return false;
|
||||
}
|
||||
// Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
|
||||
auto stack = MakeSpan(tx.witness.vtxinwit[i].scriptWitness.stack);
|
||||
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
|
||||
// Annexes are nonstandard as long as no semantics are defined for them.
|
||||
return false;
|
||||
}
|
||||
if (stack.size() >= 2) {
|
||||
// Script path spend (2 or more stack elements after removing optional annex)
|
||||
const auto& control_block = SpanPopBack(stack);
|
||||
SpanPopBack(stack); // Ignore script
|
||||
if (control_block.empty()) return false; // Empty control block is invalid
|
||||
if ((control_block[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
|
||||
// Leaf version 0xc0 (aka Tapscript, see BIP 342)
|
||||
for (const auto& item : stack) {
|
||||
if (item.size() > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) return false;
|
||||
}
|
||||
}
|
||||
} else if (stack.size() == 1) {
|
||||
// Key path spend (1 stack element after removing optional annex)
|
||||
// (no policy rules apply)
|
||||
} else {
|
||||
// 0 stack elements; this is already invalid by consensus rules
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
|
|||
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
|
||||
/** The maximum size of each witness stack item in a standard P2WSH script */
|
||||
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
|
||||
/** The maximum size of each witness stack item in a standard BIP 342 script (Taproot, leaf version 0xc0) */
|
||||
static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
|
||||
/** The maximum size of a standard witnessScript */
|
||||
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
||||
/** Min feerate for defining dust. Historically this has been based on the
|
||||
|
|
@ -71,7 +73,11 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
|
|||
SCRIPT_VERIFY_WITNESS |
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
|
||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE;
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE |
|
||||
SCRIPT_VERIFY_TAPROOT |
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
|
||||
|
||||
/** For convenience, standard but not mandatory verify flags. */
|
||||
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_recovery.h>
|
||||
#include <secp256k1_schnorrsig.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -166,6 +167,27 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
|
|||
return 1;
|
||||
}
|
||||
|
||||
XOnlyPubKey::XOnlyPubKey(Span<const unsigned char> bytes)
|
||||
{
|
||||
assert(bytes.size() == 32);
|
||||
std::copy(bytes.begin(), bytes.end(), m_keydata.begin());
|
||||
}
|
||||
|
||||
bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const
|
||||
{
|
||||
assert(sigbytes.size() == 64);
|
||||
secp256k1_xonly_pubkey pubkey;
|
||||
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data())) return false;
|
||||
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
|
||||
}
|
||||
|
||||
bool XOnlyPubKey::CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const
|
||||
{
|
||||
secp256k1_xonly_pubkey base_point;
|
||||
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &base_point, base.data())) return false;
|
||||
return secp256k1_xonly_pubkey_tweak_add_check(secp256k1_context_verify, m_keydata.begin(), parity, &base_point, hash.begin());
|
||||
}
|
||||
|
||||
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
|
||||
if (!IsValid())
|
||||
return false;
|
||||
|
|
|
|||
24
src/pubkey.h
24
src/pubkey.h
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <hash.h>
|
||||
#include <serialize.h>
|
||||
#include <span.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
|
@ -169,7 +170,7 @@ public:
|
|||
/*
|
||||
* Check syntactic correctness.
|
||||
*
|
||||
* Note that this is consensus critical as CheckSig() calls it!
|
||||
* Note that this is consensus critical as CheckECDSASignature() calls it!
|
||||
*/
|
||||
bool IsValid() const
|
||||
{
|
||||
|
|
@ -206,6 +207,27 @@ public:
|
|||
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, std::vector<unsigned char>* tweak = nullptr /* ELEMENTS: vector of key tweak values that are filled out if non-null */) const;
|
||||
};
|
||||
|
||||
class XOnlyPubKey
|
||||
{
|
||||
private:
|
||||
uint256 m_keydata;
|
||||
|
||||
public:
|
||||
/** Construct an x-only pubkey from exactly 32 bytes. */
|
||||
XOnlyPubKey(Span<const unsigned char> bytes);
|
||||
|
||||
/** Verify a Schnorr signature against this public key.
|
||||
*
|
||||
* sigbytes must be exactly 64 bytes.
|
||||
*/
|
||||
bool VerifySchnorr(const uint256& msg, Span<const unsigned char> sigbytes) const;
|
||||
bool CheckPayToContract(const XOnlyPubKey& base, const uint256& hash, bool parity) const;
|
||||
|
||||
const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); }
|
||||
const unsigned char* data() const { return m_keydata.begin(); }
|
||||
size_t size() const { return m_keydata.size(); }
|
||||
};
|
||||
|
||||
struct CExtPubKey {
|
||||
unsigned char nDepth;
|
||||
unsigned char vchFingerprint[4];
|
||||
|
|
|
|||
|
|
@ -1496,6 +1496,7 @@ RPCHelpMan getblockchaininfo()
|
|||
BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
|
||||
BIP9SoftForkDescPushBack(softforks, "dynafed", consensusParams, Consensus::DEPLOYMENT_DYNA_FED);
|
||||
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
|
||||
BIP9SoftForkDescPushBack(softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
|
||||
obj.pushKV("softforks", softforks);
|
||||
|
||||
obj.pushKV("warnings", GetWarnings(false).original);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public:
|
|||
bool sighash_byte;
|
||||
|
||||
SimpleSignatureChecker(const uint256& hashIn, bool sighash_byte_in) : hash(hashIn), sighash_byte(sighash_byte_in) {};
|
||||
bool CheckSig(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
{
|
||||
std::vector<unsigned char> vchSigCopy(vchSig);
|
||||
CPubKey pubkey(vchPubKey);
|
||||
|
|
|
|||
|
|
@ -350,13 +350,10 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
/** Helper for OP_CHECKSIG and OP_CHECKSIGVERIFY
|
||||
*
|
||||
* A return value of false means the script fails entirely. When true is returned, the
|
||||
* fSuccess variable indicates whether the signature check itself succeeded.
|
||||
*/
|
||||
static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
{
|
||||
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
|
||||
|
||||
// Subset of script starting at the most recent codeseparator
|
||||
CScript scriptCode(pbegincodehash, pend);
|
||||
|
||||
|
|
@ -371,7 +368,7 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
|
|||
//serror is set
|
||||
return false;
|
||||
}
|
||||
fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
|
||||
fSuccess = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
|
||||
|
||||
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
|
||||
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
|
||||
|
|
@ -379,7 +376,67 @@ static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScrip
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
assert(sigversion == SigVersion::TAPSCRIPT);
|
||||
|
||||
/*
|
||||
* The following validation sequence is consensus critical. Please note how --
|
||||
* upgradable public key versions precede other rules;
|
||||
* the script execution fails when using empty signature with invalid public key;
|
||||
* the script execution fails when using non-empty invalid signature.
|
||||
*/
|
||||
success = !sig.empty();
|
||||
if (success) {
|
||||
// Implement the sigops/witnesssize ratio test.
|
||||
// Passing with an upgradable public key version is also counted.
|
||||
assert(execdata.m_validation_weight_left_init);
|
||||
execdata.m_validation_weight_left -= VALIDATION_WEIGHT_PER_SIGOP_PASSED;
|
||||
if (execdata.m_validation_weight_left < 0) {
|
||||
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
|
||||
}
|
||||
}
|
||||
if (pubkey.size() == 0) {
|
||||
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
||||
} else if (pubkey.size() == 32) {
|
||||
if (success && !checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror)) {
|
||||
return false; // serror is set
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* New public key version softforks should be defined before this `else` block.
|
||||
* Generally, the new code should not do anything but failing the script execution. To avoid
|
||||
* consensus bugs, it should not modify any existing values (including `success`).
|
||||
*/
|
||||
if ((flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) != 0) {
|
||||
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Helper for OP_CHECKSIG, OP_CHECKSIGVERIFY, and (in Tapscript) OP_CHECKSIGADD.
|
||||
*
|
||||
* A return value of false means the script fails entirely. When true is returned, the
|
||||
* success variable indicates whether the signature check itself succeeded.
|
||||
*/
|
||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
switch (sigversion) {
|
||||
case SigVersion::BASE:
|
||||
case SigVersion::WITNESS_V0:
|
||||
return EvalChecksigPreTapscript(sig, pubkey, pbegincodehash, pend, flags, checker, sigversion, serror, success);
|
||||
case SigVersion::TAPSCRIPT:
|
||||
return EvalChecksigTapscript(sig, pubkey, execdata, flags, checker, sigversion, serror, success);
|
||||
case SigVersion::TAPROOT:
|
||||
// Key path spending in Taproot has no script, so this is unreachable.
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
static const CScriptNum bnZero(0);
|
||||
static const CScriptNum bnOne(1);
|
||||
|
|
@ -389,6 +446,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
static const valtype vchZero(0);
|
||||
static const valtype vchTrue(1, 1);
|
||||
|
||||
// sigversion cannot be TAPROOT here, as it admits no script execution.
|
||||
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0 || sigversion == SigVersion::TAPSCRIPT);
|
||||
|
||||
CScript::const_iterator pc = script.begin();
|
||||
CScript::const_iterator pend = script.end();
|
||||
CScript::const_iterator pbegincodehash = script.begin();
|
||||
|
|
@ -397,15 +457,18 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
ConditionStack vfExec;
|
||||
std::vector<valtype> altstack;
|
||||
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
|
||||
if (script.size() > MAX_SCRIPT_SIZE)
|
||||
if ((sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) && script.size() > MAX_SCRIPT_SIZE) {
|
||||
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
|
||||
}
|
||||
int nOpCount = 0;
|
||||
bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0;
|
||||
uint32_t opcode_pos = 0;
|
||||
execdata.m_codeseparator_pos = 0xFFFFFFFFUL;
|
||||
execdata.m_codeseparator_pos_init = true;
|
||||
|
||||
try
|
||||
{
|
||||
while (pc < pend)
|
||||
{
|
||||
for (; pc < pend; ++opcode_pos) {
|
||||
bool fExec = vfExec.all_true();
|
||||
|
||||
//
|
||||
|
|
@ -416,9 +479,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
|
||||
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
|
||||
|
||||
// Note how OP_RESERVED does not count towards the opcode limit.
|
||||
if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT)
|
||||
return set_error(serror, SCRIPT_ERR_OP_COUNT);
|
||||
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) {
|
||||
// Note how OP_RESERVED does not count towards the opcode limit.
|
||||
if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) {
|
||||
return set_error(serror, SCRIPT_ERR_OP_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
// commented out opcodes are re-enabled in Elements
|
||||
|
|
@ -580,6 +646,15 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
if (stack.size() < 1)
|
||||
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
|
||||
valtype& vch = stacktop(-1);
|
||||
// Tapscript requires minimal IF/NOTIF inputs as a consensus rule.
|
||||
if (sigversion == SigVersion::TAPSCRIPT) {
|
||||
// The input argument to the OP_IF and OP_NOTIF opcodes must be either
|
||||
// exactly 0 (the empty vector) or exactly 1 (the one-byte vector with value 1).
|
||||
if (vch.size() > 1 || (vch.size() == 1 && vch[0] != 1)) {
|
||||
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_MINIMALIF);
|
||||
}
|
||||
}
|
||||
// Under witness v0 rules it is only a policy rule, enabled through SCRIPT_VERIFY_MINIMALIF.
|
||||
if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) {
|
||||
if (vch.size() > 1)
|
||||
return set_error(serror, SCRIPT_ERR_MINIMALIF);
|
||||
|
|
@ -1279,6 +1354,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
|
||||
// Hash starts after the code separator
|
||||
pbegincodehash = pc;
|
||||
execdata.m_codeseparator_pos = opcode_pos;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1293,7 +1369,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
valtype& vchPubKey = stacktop(-1);
|
||||
|
||||
bool fSuccess = true;
|
||||
if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, flags, checker, sigversion, serror, fSuccess)) return false;
|
||||
if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, fSuccess)) return false;
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
stack.push_back(fSuccess ? vchTrue : vchFalse);
|
||||
|
|
@ -1307,9 +1383,32 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
}
|
||||
break;
|
||||
|
||||
case OP_CHECKSIGADD:
|
||||
{
|
||||
// OP_CHECKSIGADD is only available in Tapscript
|
||||
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
|
||||
|
||||
// (sig num pubkey -- num)
|
||||
if (stack.size() < 3) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
|
||||
|
||||
const valtype& sig = stacktop(-3);
|
||||
const CScriptNum num(stacktop(-2), fRequireMinimal);
|
||||
const valtype& pubkey = stacktop(-1);
|
||||
|
||||
bool success = true;
|
||||
if (!EvalChecksig(sig, pubkey, pbegincodehash, pend, execdata, flags, checker, sigversion, serror, success)) return false;
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
stack.push_back((num + (success ? 1 : 0)).getvch());
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_CHECKMULTISIG:
|
||||
case OP_CHECKMULTISIGVERIFY:
|
||||
{
|
||||
if (sigversion == SigVersion::TAPSCRIPT) return set_error(serror, SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG);
|
||||
|
||||
// ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
|
||||
|
||||
int i = 1;
|
||||
|
|
@ -1367,7 +1466,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
}
|
||||
|
||||
// Check signature
|
||||
bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
|
||||
bool fOk = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion);
|
||||
|
||||
if (fOk) {
|
||||
isig++;
|
||||
|
|
@ -1528,6 +1627,12 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
return set_success(serror);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
{
|
||||
ScriptExecutionData execdata;
|
||||
return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
|
|
@ -1678,36 +1783,184 @@ uint256 GetOutputsSHA256(const T& txTo)
|
|||
return ss.GetSHA256();
|
||||
}
|
||||
|
||||
/** Compute the (single) SHA256 of the concatenation of all amounts spent by a tx. */
|
||||
uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
for (const auto& txout : outputs_spent) {
|
||||
ss << txout.nValue;
|
||||
}
|
||||
return ss.GetSHA256();
|
||||
}
|
||||
|
||||
/** Compute the (single) SHA256 of the concatenation of all scriptPubKeys spent by a tx. */
|
||||
uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent)
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
for (const auto& txout : outputs_spent) {
|
||||
ss << txout.scriptPubKey;
|
||||
}
|
||||
return ss.GetSHA256();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
template <class T>
|
||||
void PrecomputedTransactionData::Init(const T& txTo)
|
||||
void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
|
||||
{
|
||||
assert(!m_ready);
|
||||
assert(!m_spent_outputs_ready);
|
||||
|
||||
// Cache is calculated only for transactions with witness
|
||||
if (txTo.HasWitness()) {
|
||||
hashPrevouts = SHA256Uint256(GetPrevoutsSHA256(txTo));
|
||||
hashSequence = SHA256Uint256(GetSequencesSHA256(txTo));
|
||||
hashIssuance = SHA256Uint256(GetIssuanceSHA256(txTo));
|
||||
hashOutputs = SHA256Uint256(GetOutputsSHA256(txTo));
|
||||
m_spent_outputs = std::move(spent_outputs);
|
||||
if (!m_spent_outputs.empty()) {
|
||||
assert(m_spent_outputs.size() == txTo.vin.size());
|
||||
m_spent_outputs_ready = true;
|
||||
}
|
||||
|
||||
m_ready = true;
|
||||
// Determine which precomputation-impacting features this transaction uses.
|
||||
bool uses_bip143_segwit = false;
|
||||
bool uses_bip341_taproot = false;
|
||||
for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) {
|
||||
if (inpos < txTo.witness.vtxinwit.size() && !txTo.witness.vtxinwit[inpos].scriptWitness.IsNull()) {
|
||||
if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE &&
|
||||
m_spent_outputs[inpos].scriptPubKey[0] == OP_1) {
|
||||
// Treat every witness-bearing spend with 34-byte scriptPubKey that starts with OP_1 as a Taproot
|
||||
// spend. This only works if spent_outputs was provided as well, but if it wasn't, actual validation
|
||||
// will fail anyway. Note that this branch may trigger for scriptPubKeys that aren't actually segwit
|
||||
// but in that case validation will fail as SCRIPT_ERR_WITNESS_UNEXPECTED anyway.
|
||||
uses_bip341_taproot = true;
|
||||
} else {
|
||||
// Treat every spend that's not known to native witness v1 as a Witness v0 spend. This branch may
|
||||
// also be taken for unknown witness versions, but it is harmless, and being precise would require
|
||||
// P2SH evaluation to find the redeemScript.
|
||||
uses_bip143_segwit = true;
|
||||
}
|
||||
}
|
||||
if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all.
|
||||
}
|
||||
|
||||
if (uses_bip143_segwit || uses_bip341_taproot) {
|
||||
// Computations shared between both sighash schemes.
|
||||
m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
|
||||
m_sequences_single_hash = GetSequencesSHA256(txTo);
|
||||
m_outputs_single_hash = GetOutputsSHA256(txTo);
|
||||
}
|
||||
if (uses_bip143_segwit) {
|
||||
hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
|
||||
hashSequence = SHA256Uint256(m_sequences_single_hash);
|
||||
hashIssuance = SHA256Uint256(GetIssuanceSHA256(txTo));
|
||||
hashOutputs = SHA256Uint256(m_outputs_single_hash);
|
||||
m_bip143_segwit_ready = true;
|
||||
}
|
||||
if (uses_bip341_taproot) {
|
||||
m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs);
|
||||
m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs);
|
||||
m_bip341_taproot_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
|
||||
{
|
||||
Init(txTo);
|
||||
Init(txTo, {});
|
||||
}
|
||||
|
||||
// explicit instantiation
|
||||
template void PrecomputedTransactionData::Init(const CTransaction& txTo);
|
||||
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
|
||||
template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
|
||||
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
|
||||
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
|
||||
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
|
||||
|
||||
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
|
||||
static const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
|
||||
static const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
|
||||
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
|
||||
|
||||
template<typename T>
|
||||
bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache)
|
||||
{
|
||||
uint8_t ext_flag, key_version;
|
||||
switch (sigversion) {
|
||||
case SigVersion::TAPROOT:
|
||||
ext_flag = 0;
|
||||
// key_version is not used and left uninitialized.
|
||||
break;
|
||||
case SigVersion::TAPSCRIPT:
|
||||
ext_flag = 1;
|
||||
// key_version must be 0 for now, representing the current version of
|
||||
// 32-byte public keys in the tapscript signature opcode execution.
|
||||
// An upgradable public key version (with a size not 32-byte) may
|
||||
// request a different key_version with a new sigversion.
|
||||
key_version = 0;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
assert(in_pos < tx_to.vin.size());
|
||||
assert(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready);
|
||||
|
||||
CHashWriter ss = HASHER_TAPSIGHASH;
|
||||
|
||||
// Epoch
|
||||
static constexpr uint8_t EPOCH = 0;
|
||||
ss << EPOCH;
|
||||
|
||||
// Hash type
|
||||
const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL
|
||||
const uint8_t input_type = hash_type & SIGHASH_INPUT_MASK;
|
||||
if (!(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83))) return false;
|
||||
ss << hash_type;
|
||||
|
||||
// Transaction level data
|
||||
ss << tx_to.nVersion;
|
||||
ss << tx_to.nLockTime;
|
||||
if (input_type != SIGHASH_ANYONECANPAY) {
|
||||
ss << cache.m_prevouts_single_hash;
|
||||
ss << cache.m_spent_amounts_single_hash;
|
||||
ss << cache.m_spent_scripts_single_hash;
|
||||
ss << cache.m_sequences_single_hash;
|
||||
}
|
||||
if (output_type == SIGHASH_ALL) {
|
||||
ss << cache.m_outputs_single_hash;
|
||||
}
|
||||
|
||||
// Data about the input/prevout being spent
|
||||
assert(execdata.m_annex_init);
|
||||
const bool have_annex = execdata.m_annex_present;
|
||||
const uint8_t spend_type = (ext_flag << 1) + (have_annex ? 1 : 0); // The low bit indicates whether an annex is present.
|
||||
ss << spend_type;
|
||||
if (input_type == SIGHASH_ANYONECANPAY) {
|
||||
ss << tx_to.vin[in_pos].prevout;
|
||||
ss << cache.m_spent_outputs[in_pos];
|
||||
ss << tx_to.vin[in_pos].nSequence;
|
||||
} else {
|
||||
ss << in_pos;
|
||||
}
|
||||
if (have_annex) {
|
||||
ss << execdata.m_annex_hash;
|
||||
}
|
||||
|
||||
// Data about the output (if only one).
|
||||
if (output_type == SIGHASH_SINGLE) {
|
||||
if (in_pos >= tx_to.vout.size()) return false;
|
||||
CHashWriter sha_single_output(SER_GETHASH, 0);
|
||||
sha_single_output << tx_to.vout[in_pos];
|
||||
ss << sha_single_output.GetSHA256();
|
||||
}
|
||||
|
||||
// Additional data for BIP 342 signatures
|
||||
if (sigversion == SigVersion::TAPSCRIPT) {
|
||||
assert(execdata.m_tapleaf_hash_init);
|
||||
ss << execdata.m_tapleaf_hash;
|
||||
ss << key_version;
|
||||
assert(execdata.m_codeseparator_pos_init);
|
||||
ss << execdata.m_codeseparator_pos;
|
||||
}
|
||||
|
||||
hash_out = ss.GetSHA256();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
{
|
||||
|
|
@ -1718,7 +1971,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
uint256 hashSequence;
|
||||
uint256 hashIssuance;
|
||||
uint256 hashOutputs;
|
||||
const bool cacheready = cache && cache->m_ready;
|
||||
const bool cacheready = cache && cache->m_bip143_segwit_ready;
|
||||
|
||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||
hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo));
|
||||
|
|
@ -1792,13 +2045,19 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
bool GenericTransactionSignatureChecker<T>::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
return pubkey.Verify(sighash, vchSig);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
bool GenericTransactionSignatureChecker<T>::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
return pubkey.VerifySchnorr(sighash, sig);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
{
|
||||
CPubKey pubkey(vchPubKey);
|
||||
if (!pubkey.IsValid())
|
||||
|
|
@ -1813,12 +2072,40 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned
|
|||
|
||||
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
|
||||
|
||||
if (!VerifySignature(vchSig, pubkey, sighash))
|
||||
if (!VerifyECDSASignature(vchSig, pubkey, sighash))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey_in, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror) const
|
||||
{
|
||||
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
|
||||
// Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this.
|
||||
assert(pubkey_in.size() == 32);
|
||||
// Note that in Tapscript evaluation, empty signatures are treated specially (invalid signature that does not
|
||||
// abort script execution). This is implemented in EvalChecksigTapscript, which won't invoke
|
||||
// CheckSchnorrSignature in that case. In other contexts, they are invalid like every other signature with
|
||||
// size different from 64 or 65.
|
||||
if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE);
|
||||
|
||||
XOnlyPubKey pubkey{pubkey_in};
|
||||
|
||||
uint8_t hashtype = SIGHASH_DEFAULT;
|
||||
if (sig.size() == 65) {
|
||||
hashtype = SpanPopBack(sig);
|
||||
if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
|
||||
}
|
||||
uint256 sighash;
|
||||
assert(this->txdata);
|
||||
if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata)) {
|
||||
return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE);
|
||||
}
|
||||
if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
|
||||
{
|
||||
|
|
@ -1907,17 +2194,39 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
|
|||
template class GenericTransactionSignatureChecker<CTransaction>;
|
||||
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
||||
static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
|
||||
|
||||
if (sigversion == SigVersion::TAPSCRIPT) {
|
||||
// OP_SUCCESSx processing overrides everything, including stack element size limits
|
||||
CScript::const_iterator pc = scriptPubKey.begin();
|
||||
while (pc < scriptPubKey.end()) {
|
||||
opcodetype opcode;
|
||||
if (!scriptPubKey.GetOp(pc, opcode)) {
|
||||
// Note how this condition would not be reached if an unknown OP_SUCCESSx was found
|
||||
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
|
||||
}
|
||||
// New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
|
||||
if (IsOpSuccess(opcode)) {
|
||||
if (flags & SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS) {
|
||||
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
|
||||
}
|
||||
return set_success(serror);
|
||||
}
|
||||
}
|
||||
|
||||
// Tapscript enforces initial stack size limits (altstack is empty here)
|
||||
if (stack.size() > MAX_STACK_SIZE) return set_error(serror, SCRIPT_ERR_STACK_SIZE);
|
||||
}
|
||||
|
||||
// Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
|
||||
for (const valtype& elem : stack) {
|
||||
if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
|
||||
}
|
||||
|
||||
// Run the script interpreter.
|
||||
if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror)) return false;
|
||||
if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, execdata, serror)) return false;
|
||||
|
||||
// Scripts inside witness implicitly require cleanstack behaviour
|
||||
if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK);
|
||||
|
|
@ -1925,40 +2234,104 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, const std::vector<unsigned char>& program, const CScript& script, uint256& tapleaf_hash)
|
||||
{
|
||||
CScript scriptPubKey;
|
||||
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
|
||||
const XOnlyPubKey p{uint256(std::vector<unsigned char>(control.begin() + 1, control.begin() + TAPROOT_CONTROL_BASE_SIZE))};
|
||||
const XOnlyPubKey q{uint256(program)};
|
||||
tapleaf_hash = (CHashWriter(HASHER_TAPLEAF) << uint8_t(control[0] & TAPROOT_LEAF_MASK) << script).GetSHA256();
|
||||
uint256 k = tapleaf_hash;
|
||||
for (int i = 0; i < path_len; ++i) {
|
||||
CHashWriter ss_branch{HASHER_TAPBRANCH};
|
||||
Span<const unsigned char> node(control.data() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE);
|
||||
if (std::lexicographical_compare(k.begin(), k.end(), node.begin(), node.end())) {
|
||||
ss_branch << k << node;
|
||||
} else {
|
||||
ss_branch << node << k;
|
||||
}
|
||||
k = ss_branch.GetSHA256();
|
||||
}
|
||||
k = (CHashWriter(HASHER_TAPTWEAK) << MakeSpan(p) << k).GetSHA256();
|
||||
return q.CheckPayToContract(p, k, 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)
|
||||
Span<const valtype> stack{witness.stack};
|
||||
ScriptExecutionData execdata;
|
||||
|
||||
if (witversion == 0) {
|
||||
if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
|
||||
// Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness
|
||||
// BIP141 P2WSH: 32-byte witness v0 program (which encodes SHA256(script))
|
||||
if (stack.size() == 0) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
|
||||
}
|
||||
const valtype& script_bytes = SpanPopBack(stack);
|
||||
scriptPubKey = CScript(script_bytes.begin(), script_bytes.end());
|
||||
uint256 hashScriptPubKey;
|
||||
CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin());
|
||||
if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) {
|
||||
exec_script = CScript(script_bytes.begin(), script_bytes.end());
|
||||
uint256 hash_exec_script;
|
||||
CSHA256().Write(&exec_script[0], exec_script.size()).Finalize(hash_exec_script.begin());
|
||||
if (memcmp(hash_exec_script.begin(), program.data(), 32)) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
|
||||
}
|
||||
return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
|
||||
} else if (program.size() == WITNESS_V0_KEYHASH_SIZE) {
|
||||
// Special case for pay-to-pubkeyhash; signature + pubkey in witness
|
||||
// BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey))
|
||||
if (stack.size() != 2) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness
|
||||
}
|
||||
scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror);
|
||||
exec_script << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::WITNESS_V0, checker, execdata, serror);
|
||||
} else {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH);
|
||||
}
|
||||
} else if (witversion == 1 && program.size() == WITNESS_V1_TAPROOT_SIZE && !is_p2sh) {
|
||||
// BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
|
||||
if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
|
||||
if (stack.size() == 0) return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY);
|
||||
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
|
||||
// Drop annex (this is non-standard; see IsWitnessStandard)
|
||||
const valtype& annex = SpanPopBack(stack);
|
||||
execdata.m_annex_hash = (CHashWriter(SER_GETHASH, 0) << annex).GetSHA256();
|
||||
execdata.m_annex_present = true;
|
||||
} else {
|
||||
execdata.m_annex_present = false;
|
||||
}
|
||||
execdata.m_annex_init = true;
|
||||
if (stack.size() == 1) {
|
||||
// Key path spending (stack size is 1 after removing optional annex)
|
||||
if (!checker.CheckSchnorrSignature(stack.front(), program, SigVersion::TAPROOT, execdata, serror)) {
|
||||
return false; // serror is set
|
||||
}
|
||||
return set_success(serror);
|
||||
} else {
|
||||
// Script path spending (stack size is >1 after removing optional annex)
|
||||
const valtype& control = SpanPopBack(stack);
|
||||
const valtype& script_bytes = SpanPopBack(stack);
|
||||
exec_script = CScript(script_bytes.begin(), script_bytes.end());
|
||||
if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) {
|
||||
return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
|
||||
}
|
||||
if (!VerifyTaprootCommitment(control, program, exec_script, execdata.m_tapleaf_hash)) {
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH);
|
||||
}
|
||||
execdata.m_tapleaf_hash_init = true;
|
||||
if ((control[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
|
||||
// Tapscript (leaf version 0xc0)
|
||||
execdata.m_validation_weight_left = ::GetSerializeSize(witness.stack, PROTOCOL_VERSION) + VALIDATION_WEIGHT_OFFSET;
|
||||
execdata.m_validation_weight_left_init = true;
|
||||
return ExecuteWitnessScript(stack, exec_script, flags, SigVersion::TAPSCRIPT, checker, execdata, serror);
|
||||
}
|
||||
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) {
|
||||
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION);
|
||||
}
|
||||
return set_success(serror);
|
||||
}
|
||||
} else {
|
||||
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) {
|
||||
return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM);
|
||||
}
|
||||
// Higher version witness scripts return true for future softfork compatibility
|
||||
// Other version/size/p2sh combinations return true for future softfork compatibility
|
||||
return true;
|
||||
}
|
||||
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
|
||||
|
|
@ -2004,7 +2377,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
|
|||
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED);
|
||||
}
|
||||
if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
|
||||
if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ false)) {
|
||||
return false;
|
||||
}
|
||||
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
|
||||
|
|
@ -2049,7 +2422,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
|
|||
// reintroduce malleability.
|
||||
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH);
|
||||
}
|
||||
if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
|
||||
if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, /* is_p2sh */ true)) {
|
||||
return false;
|
||||
}
|
||||
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
|
||||
|
|
|
|||
|
|
@ -7,14 +7,17 @@
|
|||
#define BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
||||
#include <script/script_error.h>
|
||||
#include <span.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
class CPubKey;
|
||||
class XOnlyPubKey;
|
||||
class CScript;
|
||||
class CTransaction;
|
||||
class CTxOut;
|
||||
class uint256;
|
||||
|
||||
/** Signature hash types/flags */
|
||||
|
|
@ -24,6 +27,10 @@ enum
|
|||
SIGHASH_NONE = 2,
|
||||
SIGHASH_SINGLE = 3,
|
||||
SIGHASH_ANYONECANPAY = 0x80,
|
||||
|
||||
SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL
|
||||
SIGHASH_OUTPUT_MASK = 3,
|
||||
SIGHASH_INPUT_MASK = 0x80,
|
||||
};
|
||||
|
||||
/** Script verification flags.
|
||||
|
|
@ -79,6 +86,8 @@ enum
|
|||
// "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
|
||||
// (BIP62 rule 6)
|
||||
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
|
||||
// Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
|
||||
// consensus rules. It is automatic there and does not need this flag.
|
||||
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
|
||||
|
||||
// Verify CHECKLOCKTIMEVERIFY
|
||||
|
|
@ -101,6 +110,8 @@ enum
|
|||
|
||||
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
||||
//
|
||||
// Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
|
||||
// rules. It is automatic there and does not depend on this flag.
|
||||
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
|
||||
|
||||
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
|
||||
|
|
@ -115,22 +126,52 @@ enum
|
|||
//
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
|
||||
|
||||
// Taproot/Tapscript validation (BIPs 341 & 342)
|
||||
//
|
||||
SCRIPT_VERIFY_TAPROOT = (1U << 17),
|
||||
|
||||
// Making unknown Taproot leaf versions non-standard
|
||||
//
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
|
||||
|
||||
// Making unknown OP_SUCCESS non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
|
||||
|
||||
// Making unknown public key versions (in BIP 342 scripts) non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
|
||||
|
||||
// Signature checking assumes no sighash byte after the DER signature
|
||||
//
|
||||
SCRIPT_NO_SIGHASH_BYTE = (1U << 17),
|
||||
SCRIPT_NO_SIGHASH_BYTE = (1U << 21),
|
||||
};
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
// BIP341 precomputed data.
|
||||
// These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-15.
|
||||
uint256 m_prevouts_single_hash;
|
||||
uint256 m_sequences_single_hash;
|
||||
uint256 m_outputs_single_hash;
|
||||
uint256 m_spent_amounts_single_hash;
|
||||
uint256 m_spent_scripts_single_hash;
|
||||
//! Whether the 5 fields above are initialized.
|
||||
bool m_bip341_taproot_ready = false;
|
||||
|
||||
// BIP143 precomputed data (double-SHA256).
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance;
|
||||
bool m_ready = false;
|
||||
//! Whether the 3 fields above are initialized.
|
||||
bool m_bip143_segwit_ready = false;
|
||||
|
||||
std::vector<CTxOut> m_spent_outputs;
|
||||
//! Whether m_spent_outputs is initialized.
|
||||
bool m_spent_outputs_ready = false;
|
||||
|
||||
PrecomputedTransactionData() = default;
|
||||
|
||||
template <class T>
|
||||
void Init(const T& tx);
|
||||
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);
|
||||
|
||||
template <class T>
|
||||
explicit PrecomputedTransactionData(const T& tx);
|
||||
|
|
@ -138,13 +179,48 @@ struct PrecomputedTransactionData
|
|||
|
||||
enum class SigVersion
|
||||
{
|
||||
BASE = 0,
|
||||
WITNESS_V0 = 1,
|
||||
BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts
|
||||
WITNESS_V0 = 1, //!< Witness v0 (P2WPKH and P2WSH); see BIP 141
|
||||
TAPROOT = 2, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341
|
||||
TAPSCRIPT = 3, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, script path spending, leaf version 0xc0; see BIP 342
|
||||
};
|
||||
|
||||
struct ScriptExecutionData
|
||||
{
|
||||
//! Whether m_tapleaf_hash is initialized.
|
||||
bool m_tapleaf_hash_init = false;
|
||||
//! The tapleaf hash.
|
||||
uint256 m_tapleaf_hash;
|
||||
|
||||
//! Whether m_codeseparator_pos is initialized.
|
||||
bool m_codeseparator_pos_init = false;
|
||||
//! Opcode position of the last executed OP_CODESEPARATOR (or 0xFFFFFFFF if none executed).
|
||||
uint32_t m_codeseparator_pos;
|
||||
|
||||
//! Whether m_annex_present and (when needed) m_annex_hash are initialized.
|
||||
bool m_annex_init = false;
|
||||
//! Whether an annex is present.
|
||||
bool m_annex_present;
|
||||
//! Hash of the annex data.
|
||||
uint256 m_annex_hash;
|
||||
|
||||
//! Whether m_validation_weight_left is initialized.
|
||||
bool m_validation_weight_left_init = false;
|
||||
//! How much validation weight is left (decremented for every successful non-empty signature check).
|
||||
int64_t m_validation_weight_left;
|
||||
};
|
||||
|
||||
/** Signature hash sizes */
|
||||
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
|
||||
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
|
||||
static constexpr size_t WITNESS_V1_TAPROOT_SIZE = 32;
|
||||
|
||||
static constexpr uint8_t TAPROOT_LEAF_MASK = 0xfe;
|
||||
static constexpr uint8_t TAPROOT_LEAF_TAPSCRIPT = 0xc0;
|
||||
static constexpr size_t TAPROOT_CONTROL_BASE_SIZE = 33;
|
||||
static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32;
|
||||
static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128;
|
||||
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
|
|
@ -152,7 +228,12 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
class BaseSignatureChecker
|
||||
{
|
||||
public:
|
||||
virtual bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
virtual bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -180,12 +261,14 @@ private:
|
|||
const PrecomputedTransactionData* txdata;
|
||||
|
||||
protected:
|
||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
virtual bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
virtual bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const;
|
||||
|
||||
public:
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
};
|
||||
|
|
@ -193,6 +276,7 @@ public:
|
|||
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
|
||||
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ std::string GetOpName(opcodetype opcode)
|
|||
case OP_NOP9 : return "OP_NOP9";
|
||||
case OP_NOP10 : return "OP_NOP10";
|
||||
|
||||
// Opcode added by BIP 342 (Tapscript)
|
||||
case OP_CHECKSIGADD : return "OP_CHECKSIGADD";
|
||||
|
||||
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
|
||||
|
||||
default:
|
||||
|
|
@ -405,3 +408,11 @@ bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator en
|
|||
opcodeRet = static_cast<opcodetype>(opcode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsOpSuccess(const opcodetype& opcode)
|
||||
{
|
||||
return opcode == 80 || opcode == 98 || (opcode >= 126 && opcode <= 129) ||
|
||||
(opcode >= 131 && opcode <= 134) || (opcode >= 137 && opcode <= 138) ||
|
||||
(opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 153) ||
|
||||
(opcode >= 187 && opcode <= 254);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,17 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20
|
|||
// SEQUENCE_FINAL).
|
||||
static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
|
||||
|
||||
// Tag for input annex. If there are at least two witness elements for a transaction input,
|
||||
// and the first byte of the last element is 0x50, this last element is called annex, and
|
||||
// has meanings independent of the script
|
||||
static constexpr unsigned int ANNEX_TAG = 0x50;
|
||||
|
||||
// Validation weight per passing signature (Tapscript only, see BIP 342).
|
||||
static constexpr uint64_t VALIDATION_WEIGHT_PER_SIGOP_PASSED = 50;
|
||||
|
||||
// How much weight budget is added to the witness size (Tapscript only, see BIP 342).
|
||||
static constexpr uint64_t VALIDATION_WEIGHT_OFFSET = 50;
|
||||
|
||||
// ELEMENTS:
|
||||
// Number of confirms on parent chain required to confirm on sidechain.
|
||||
static const unsigned int DEFAULT_PEGIN_CONFIRMATION_DEPTH = 8;
|
||||
|
|
@ -199,6 +210,9 @@ enum opcodetype
|
|||
OP_NOP9 = 0xb8,
|
||||
OP_NOP10 = 0xb9,
|
||||
|
||||
// Opcode added by BIP 342 (Tapscript)
|
||||
OP_CHECKSIGADD = 0xba,
|
||||
|
||||
OP_INVALIDOPCODE = 0xff,
|
||||
};
|
||||
|
||||
|
|
@ -581,4 +595,7 @@ struct CScriptWitness
|
|||
uint32_t GetSerializedSize() const;
|
||||
};
|
||||
|
||||
/** Test for OP_SUCCESSx opcodes as defined by BIP342. */
|
||||
bool IsOpSuccess(const opcodetype& opcode);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_SCRIPT_H
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@ std::string ScriptErrorString(const ScriptError serror)
|
|||
return "NOPx reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:
|
||||
return "Witness version reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION:
|
||||
return "Taproot version reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_DISCOURAGE_OP_SUCCESS:
|
||||
return "OP_SUCCESSx reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE:
|
||||
return "Public key version reserved for soft-fork upgrades";
|
||||
case SCRIPT_ERR_PUBKEYTYPE:
|
||||
return "Public key is neither compressed or uncompressed";
|
||||
case SCRIPT_ERR_CLEANSTACK:
|
||||
|
|
@ -91,6 +97,20 @@ std::string ScriptErrorString(const ScriptError serror)
|
|||
return "Witness provided for non-witness script";
|
||||
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
|
||||
return "Using non-compressed keys in segwit";
|
||||
case SCRIPT_ERR_SCHNORR_SIG_SIZE:
|
||||
return "Invalid Schnorr signature size";
|
||||
case SCRIPT_ERR_SCHNORR_SIG_HASHTYPE:
|
||||
return "Invalid Schnorr signature hash type";
|
||||
case SCRIPT_ERR_SCHNORR_SIG:
|
||||
return "Invalid Schnorr signature";
|
||||
case SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE:
|
||||
return "Invalid Taproot control block size";
|
||||
case SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT:
|
||||
return "Too much signature validation relative to witness weight";
|
||||
case SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG:
|
||||
return "OP_CHECKMULTISIG(VERIFY) is not available in tapscript";
|
||||
case SCRIPT_ERR_TAPSCRIPT_MINIMALIF:
|
||||
return "OP_IF/NOTIF argument must be minimal in tapscript";
|
||||
case SCRIPT_ERR_OP_CODESEPARATOR:
|
||||
return "Using OP_CODESEPARATOR in non-witness script";
|
||||
case SCRIPT_ERR_SIG_FINDANDDELETE:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ typedef enum ScriptError_t
|
|||
/* softfork safeness */
|
||||
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
|
||||
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
|
||||
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
|
||||
SCRIPT_ERR_DISCOURAGE_OP_SUCCESS,
|
||||
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
|
||||
|
||||
/* segregated witness */
|
||||
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH,
|
||||
|
|
@ -66,6 +69,15 @@ typedef enum ScriptError_t
|
|||
SCRIPT_ERR_WITNESS_UNEXPECTED,
|
||||
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
|
||||
|
||||
/* Taproot */
|
||||
SCRIPT_ERR_SCHNORR_SIG_SIZE,
|
||||
SCRIPT_ERR_SCHNORR_SIG_HASHTYPE,
|
||||
SCRIPT_ERR_SCHNORR_SIG,
|
||||
SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE,
|
||||
SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT,
|
||||
SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG,
|
||||
SCRIPT_ERR_TAPSCRIPT_MINIMALIF,
|
||||
|
||||
/* Constant scriptCode */
|
||||
SCRIPT_ERR_OP_CODESEPARATOR,
|
||||
SCRIPT_ERR_SIG_FINDANDDELETE,
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ namespace {
|
|||
class CSignatureCache
|
||||
{
|
||||
private:
|
||||
//! Entries are SHA256(nonce || signature hash || public key || signature):
|
||||
CSHA256 m_salted_hasher;
|
||||
//! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
|
||||
CSHA256 m_salted_hasher_ecdsa;
|
||||
CSHA256 m_salted_hasher_schnorr;
|
||||
CSHA256 m_salted_hasher_range_proof;
|
||||
CSHA256 m_salted_hasher_surjection_proof;
|
||||
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
|
||||
map_type setValid;
|
||||
boost::shared_mutex cs_sigcache;
|
||||
|
|
@ -34,25 +37,43 @@ public:
|
|||
uint256 nonce = GetRandHash();
|
||||
// We want the nonce to be 64 bytes long to force the hasher to process
|
||||
// this chunk, which makes later hash computations more efficient. We
|
||||
// just write our 32-byte entropy twice to fill the 64 bytes.
|
||||
m_salted_hasher.Write(nonce.begin(), 32);
|
||||
m_salted_hasher.Write(nonce.begin(), 32);
|
||||
// just write our 32-byte entropy, and then pad with 'E' for ECDSA and
|
||||
// 'S' for Schnorr (followed by 0 bytes).
|
||||
static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
|
||||
static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
|
||||
static constexpr unsigned char PADDING_RANGE_PROOF[32] = {'r'};
|
||||
static constexpr unsigned char PADDING_SURJECTION_PROOF[32] = {'s'};
|
||||
m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
|
||||
m_salted_hasher_schnorr.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
|
||||
m_salted_hasher_range_proof.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_range_proof.Write(PADDING_RANGE_PROOF, 32);
|
||||
m_salted_hasher_surjection_proof.Write(nonce.begin(), 32);
|
||||
m_salted_hasher_surjection_proof.Write(PADDING_SURJECTION_PROOF, 32);
|
||||
}
|
||||
|
||||
void
|
||||
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
|
||||
ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
|
||||
{
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
CSHA256 hasher = m_salted_hasher_ecdsa;
|
||||
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
void
|
||||
ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey)
|
||||
{
|
||||
CSHA256 hasher = m_salted_hasher_schnorr;
|
||||
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
void ComputeEntry(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
void ComputeEntryRangeProof(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher_range_proof;
|
||||
hasher.Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
void ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher;
|
||||
void ComputeEntrySurjectionProof(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256 hasher = m_salted_hasher_surjection_proof;
|
||||
hasher.Write(hash.begin(), 32).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
|
|
@ -100,19 +121,29 @@ void InitSignatureCache()
|
|||
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
|
||||
}
|
||||
|
||||
bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
uint256 entry;
|
||||
signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
|
||||
signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
|
||||
if (signatureCache.Get(entry, !store))
|
||||
return true;
|
||||
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
|
||||
if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
|
||||
return false;
|
||||
if (store)
|
||||
signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
|
||||
{
|
||||
uint256 entry;
|
||||
signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
|
||||
if (signatureCache.Get(entry, !store)) return true;
|
||||
if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
|
||||
if (store) signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS CACHES
|
||||
|
||||
|
|
@ -141,7 +172,7 @@ void InitSurjectionproofCache()
|
|||
bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* secp256k1_ctx_verify_amounts) const
|
||||
{
|
||||
uint256 entry;
|
||||
rangeProofCache.ComputeEntry(entry, vchRangeProof, vchValueCommitment);
|
||||
rangeProofCache.ComputeEntryRangeProof(entry, vchRangeProof, vchValueCommitment);
|
||||
|
||||
if (rangeProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
|
|
@ -192,7 +223,7 @@ bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionpr
|
|||
// wtxid commits to all data including surj targets
|
||||
// we need to specify the proof and output asset point to be unique
|
||||
uint256 entry;
|
||||
surjectionProofCache.ComputeEntry(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
surjectionProofCache.ComputeEntrySurjectionProof(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
|
||||
if (surjectionProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#define BITCOIN_SCRIPT_SIGCACHE_H
|
||||
|
||||
#include <script/interpreter.h>
|
||||
#include <span.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
|
|
@ -51,7 +52,8 @@ private:
|
|||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
|
||||
};
|
||||
|
||||
void InitSignatureCache();
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
|
|||
case TxoutType::NONSTANDARD:
|
||||
case TxoutType::NULL_DATA:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
return false;
|
||||
case TxoutType::PUBKEY:
|
||||
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
|
||||
|
|
@ -267,9 +268,9 @@ private:
|
|||
|
||||
public:
|
||||
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
{
|
||||
if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
|
||||
if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) {
|
||||
CPubKey pubkey(vchPubKey);
|
||||
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
|
||||
return true;
|
||||
|
|
@ -346,7 +347,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||
for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
|
||||
const valtype& pubkey = solutions[i+1];
|
||||
// We either have a signature for this pubkey, or we have found a signature and it is valid
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
|
||||
if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckECDSASignature(sig, pubkey, next_script, sigversion)) {
|
||||
last_success_key = i + 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -411,7 +412,7 @@ class DummySignatureChecker final : public BaseSignatureChecker
|
|||
{
|
||||
public:
|
||||
DummySignatureChecker() {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
};
|
||||
const DummySignatureChecker DUMMY_CHECKER;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <pubkey.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/keyorigin.h>
|
||||
#include <span.h>
|
||||
#include <streams.h>
|
||||
|
||||
class CKey;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ std::string GetTxnOutputType(TxoutType t)
|
|||
case TxoutType::NULL_DATA: return "nulldata";
|
||||
case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
|
||||
case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
|
||||
case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
|
||||
case TxoutType::TRUE: return "true";
|
||||
case TxoutType::FEE: return "fee";
|
||||
|
|
@ -153,6 +154,11 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
|
|||
vSolutionsRet.push_back(witnessprogram);
|
||||
return TxoutType::WITNESS_V0_SCRIPTHASH;
|
||||
}
|
||||
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
return TxoutType::WITNESS_V1_TAPROOT;
|
||||
}
|
||||
if (witnessversion != 0) {
|
||||
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
|
||||
vSolutionsRet.push_back(std::move(witnessprogram));
|
||||
|
|
@ -231,7 +237,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
|
||||
addressRet = hash;
|
||||
return true;
|
||||
} else if (whichType == TxoutType::WITNESS_UNKNOWN) {
|
||||
} else if (whichType == TxoutType::WITNESS_UNKNOWN || whichType == TxoutType::WITNESS_V1_TAPROOT) {
|
||||
WitnessUnknown unk;
|
||||
unk.version = vSolutions[0][0];
|
||||
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ enum class TxoutType {
|
|||
NULL_DATA, //!< unspendable OP_RETURN script that carries data
|
||||
WITNESS_V0_SCRIPTHASH,
|
||||
WITNESS_V0_KEYHASH,
|
||||
WITNESS_V1_TAPROOT,
|
||||
WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||
TRUE, // For testing purposes only
|
||||
// ELEMENTS:
|
||||
|
|
@ -266,7 +267,8 @@ public:
|
|||
* * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
|
||||
* * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
|
||||
* * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
|
||||
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
|
||||
* * WitnessUnknown: TxoutType::WITNESS_UNKNOWN/WITNESS_V1_TAPROOT destination (P2W???)
|
||||
* (taproot outputs do not require their own type as long as no wallet support exists)
|
||||
* * NullData: TxoutType::NULL_DATA destination (OP_RETURN)
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
|
|
|
|||
200
src/test/fuzz/script_assets_test_minimizer.cpp
Normal file
200
src/test/fuzz/script_assets_test_minimizer.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// Copyright (c) 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 <test/fuzz/fuzz.h>
|
||||
|
||||
#include <primitives/transaction.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <serialize.h>
|
||||
#include <streams.h>
|
||||
#include <univalue.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// This fuzz "test" can be used to minimize test cases for script_assets_test in
|
||||
// src/test/script_tests.cpp. While it written as a fuzz test, and can be used as such,
|
||||
// fuzzing the inputs is unlikely to construct useful test cases.
|
||||
//
|
||||
// Instead, it is primarily intended to be run on a test set that was generated
|
||||
// externally, for example using test/functional/feature_taproot.py's --dumptests mode.
|
||||
// The minimized set can then be concatenated together, surrounded by '[' and ']',
|
||||
// and used as the script_assets_test.json input to the script_assets_test unit test:
|
||||
//
|
||||
// (normal build)
|
||||
// $ mkdir dump
|
||||
// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
|
||||
// $ ...
|
||||
//
|
||||
// (fuzz test build)
|
||||
// $ mkdir dump-min
|
||||
// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
|
||||
// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<unsigned char> CheckedParseHex(const std::string& str)
|
||||
{
|
||||
if (str.size() && !IsHex(str)) throw std::runtime_error("Non-hex input '" + str + "'");
|
||||
return ParseHex(str);
|
||||
}
|
||||
|
||||
CScript ScriptFromHex(const std::string& str)
|
||||
{
|
||||
std::vector<unsigned char> data = CheckedParseHex(str);
|
||||
return CScript(data.begin(), data.end());
|
||||
}
|
||||
|
||||
CMutableTransaction TxFromHex(const std::string& str)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
try {
|
||||
VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx;
|
||||
} catch (const std::ios_base::failure&) {
|
||||
throw std::runtime_error("Tx deserialization failure");
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
||||
std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
|
||||
{
|
||||
if (!univalue.isArray()) throw std::runtime_error("Prevouts must be array");
|
||||
std::vector<CTxOut> prevouts;
|
||||
for (size_t i = 0; i < univalue.size(); ++i) {
|
||||
CTxOut txout;
|
||||
try {
|
||||
VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout;
|
||||
} catch (const std::ios_base::failure&) {
|
||||
throw std::runtime_error("Prevout invalid format");
|
||||
}
|
||||
prevouts.push_back(std::move(txout));
|
||||
}
|
||||
return prevouts;
|
||||
}
|
||||
|
||||
CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
||||
{
|
||||
if (!univalue.isArray()) throw std::runtime_error("Script witness is not array");
|
||||
CScriptWitness scriptwitness;
|
||||
for (size_t i = 0; i < univalue.size(); ++i) {
|
||||
auto bytes = CheckedParseHex(univalue[i].get_str());
|
||||
scriptwitness.stack.push_back(std::move(bytes));
|
||||
}
|
||||
return scriptwitness;
|
||||
}
|
||||
|
||||
const std::map<std::string, unsigned int> FLAG_NAMES = {
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
};
|
||||
|
||||
std::vector<unsigned int> AllFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
|
||||
if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
|
||||
if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
|
||||
if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
|
||||
|
||||
// SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
|
||||
if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
|
||||
// SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
|
||||
if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
|
||||
|
||||
ret.push_back(flag);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<unsigned int> ALL_FLAGS = AllFlags();
|
||||
|
||||
unsigned int ParseScriptFlags(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return 0;
|
||||
|
||||
unsigned int flags = 0;
|
||||
std::vector<std::string> words;
|
||||
boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
|
||||
|
||||
for (const std::string& word : words)
|
||||
{
|
||||
auto it = FLAG_NAMES.find(word);
|
||||
if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word);
|
||||
flags |= it->second;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void Test(const std::string& str)
|
||||
{
|
||||
UniValue test;
|
||||
if (!test.read(str) || !test.isObject()) throw std::runtime_error("Non-object test input");
|
||||
|
||||
CMutableTransaction tx = TxFromHex(test["tx"].get_str());
|
||||
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
|
||||
if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
|
||||
size_t idx = test["index"].get_int64();
|
||||
if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
|
||||
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
bool final = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
|
||||
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
|
||||
PrecomputedTransactionData txdata;
|
||||
txdata.Init(tx, std::vector<CTxOut>(prevouts));
|
||||
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
|
||||
for (const auto flags : ALL_FLAGS) {
|
||||
// "final": true tests are valid for all flags. Others are only valid with flags that are
|
||||
// a subset of test_flags.
|
||||
if (final || ((flags & test_flags) == flags)) {
|
||||
(void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (test.exists("failure")) {
|
||||
tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
|
||||
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
|
||||
PrecomputedTransactionData txdata;
|
||||
txdata.Init(tx, std::vector<CTxOut>(prevouts));
|
||||
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
|
||||
for (const auto flags : ALL_FLAGS) {
|
||||
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
|
||||
if ((flags & test_flags) == test_flags) {
|
||||
(void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ECCVerifyHandle handle;
|
||||
|
||||
}
|
||||
|
||||
void test_one_input(const std::vector<uint8_t>& buffer)
|
||||
{
|
||||
if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
|
||||
const std::string str((const char*)buffer.data(), buffer.size() - 2);
|
||||
try {
|
||||
Test(str);
|
||||
} catch (const std::runtime_error&) {}
|
||||
}
|
||||
|
|
@ -35,11 +35,19 @@ void test_one_input(const std::vector<uint8_t>& buffer)
|
|||
const bool store = fuzzed_data_provider.ConsumeBool();
|
||||
PrecomputedTransactionData tx_data;
|
||||
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
|
||||
const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
|
||||
if (pub_key) {
|
||||
const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
|
||||
if (!random_bytes.empty()) {
|
||||
(void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
|
||||
const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
|
||||
if (random_bytes.size() == 64) {
|
||||
(void)caching_transaction_signature_checker.VerifySchnorrSignature(random_bytes, pub_key, ConsumeUInt256(fuzzed_data_provider));
|
||||
}
|
||||
} else {
|
||||
const auto random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
|
||||
const auto pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
|
||||
if (pub_key) {
|
||||
if (!random_bytes.empty()) {
|
||||
(void)caching_transaction_signature_checker.VerifyECDSASignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
{
|
||||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
|
||||
{
|
||||
return m_fuzzed_data_provider.ConsumeBool();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,4 +264,32 @@ BOOST_AUTO_TEST_CASE(pubkey_unserialize)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bip340_test_vectors)
|
||||
{
|
||||
static const std::vector<std::pair<std::array<std::string, 3>, bool>> VECTORS = {
|
||||
{{"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}, true},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}, true},
|
||||
{{"DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}, true},
|
||||
{{"25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}, true},
|
||||
{{"D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"}, true},
|
||||
{{"EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
|
||||
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"}, false},
|
||||
{{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}
|
||||
};
|
||||
|
||||
for (const auto& test : VECTORS) {
|
||||
auto pubkey = ParseHex(test.first[0]);
|
||||
auto msg = ParseHex(test.first[1]);
|
||||
auto sig = ParseHex(test.first[2]);
|
||||
BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@
|
|||
#include <test/data/script_tests.json.h>
|
||||
|
||||
#include <core_io.h>
|
||||
#include <fs.h>
|
||||
#include <key.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <script/sign.h>
|
||||
#include <script/signingprovider.h>
|
||||
#include <streams.h>
|
||||
|
|
@ -1343,13 +1345,41 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
|
|||
BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
|
||||
}
|
||||
|
||||
static CScript
|
||||
ScriptFromHex(const char* hex)
|
||||
static CScript ScriptFromHex(const std::string& str)
|
||||
{
|
||||
std::vector<unsigned char> data = ParseHex(hex);
|
||||
std::vector<unsigned char> data = ParseHex(str);
|
||||
return CScript(data.begin(), data.end());
|
||||
}
|
||||
|
||||
static CMutableTransaction TxFromHex(const std::string& str)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
|
||||
return tx;
|
||||
}
|
||||
|
||||
static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
|
||||
{
|
||||
assert(univalue.isArray());
|
||||
std::vector<CTxOut> prevouts;
|
||||
for (size_t i = 0; i < univalue.size(); ++i) {
|
||||
CTxOut txout;
|
||||
VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout;
|
||||
prevouts.push_back(std::move(txout));
|
||||
}
|
||||
return prevouts;
|
||||
}
|
||||
|
||||
static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
||||
{
|
||||
assert(univalue.isArray());
|
||||
CScriptWitness scriptwitness;
|
||||
for (size_t i = 0; i < univalue.size(); ++i) {
|
||||
auto bytes = ParseHex(univalue[i].get_str());
|
||||
scriptwitness.stack.push_back(std::move(bytes));
|
||||
}
|
||||
return scriptwitness;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
||||
{
|
||||
|
|
@ -1614,5 +1644,108 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
|
|||
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
|
||||
}
|
||||
|
||||
static std::vector<unsigned int> AllConsensusFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
|
||||
if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
|
||||
if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
|
||||
if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
|
||||
|
||||
// SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
|
||||
if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
|
||||
// SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
|
||||
if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
|
||||
|
||||
ret.push_back(flag);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
|
||||
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
|
||||
|
||||
static void AssetTest(const UniValue& test)
|
||||
{
|
||||
BOOST_CHECK(test.isObject());
|
||||
|
||||
CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
|
||||
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
|
||||
BOOST_CHECK(prevouts.size() == mtx.vin.size());
|
||||
BOOST_CHECK(prevouts.size() == mtx.witness.vtxinwit.size());
|
||||
size_t idx = test["index"].get_int64();
|
||||
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
bool fin = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
|
||||
mtx.witness.vtxinwit[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
|
||||
CTransaction tx(mtx);
|
||||
PrecomputedTransactionData txdata;
|
||||
txdata.Init(tx, std::vector<CTxOut>(prevouts));
|
||||
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
|
||||
for (const auto flags : ALL_CONSENSUS_FLAGS) {
|
||||
// "final": true tests are valid for all flags. Others are only valid with flags that are
|
||||
// a subset of test_flags.
|
||||
if (fin || ((flags & test_flags) == flags)) {
|
||||
bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.witness.vtxinwit[idx].scriptWitness, flags, txcheck, nullptr);
|
||||
BOOST_CHECK(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (test.exists("failure")) {
|
||||
mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
|
||||
mtx.witness.vtxinwit[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
|
||||
CTransaction tx(mtx);
|
||||
PrecomputedTransactionData txdata;
|
||||
txdata.Init(tx, std::vector<CTxOut>(prevouts));
|
||||
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
|
||||
for (const auto flags : ALL_CONSENSUS_FLAGS) {
|
||||
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
|
||||
if ((flags & test_flags) == test_flags) {
|
||||
bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.witness.vtxinwit[idx].scriptWitness, flags, txcheck, nullptr);
|
||||
BOOST_CHECK(!ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_assets_test)
|
||||
{
|
||||
// See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
|
||||
// the script_assets_test.json file used by this test.
|
||||
|
||||
const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
|
||||
BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
|
||||
if (dir == nullptr) return;
|
||||
auto path = fs::path(dir) / "script_assets_test.json";
|
||||
bool exists = fs::exists(path);
|
||||
BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
|
||||
if (!exists) return;
|
||||
fs::ifstream file(path);
|
||||
BOOST_CHECK(file.is_open());
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t length = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::string data(length, '\0');
|
||||
file.read(&data[0], data.size());
|
||||
UniValue tests = read_json(data);
|
||||
BOOST_CHECK(tests.isArray());
|
||||
BOOST_CHECK(tests.size() > 0);
|
||||
|
||||
for (size_t i = 0; i < tests.size(); i++) {
|
||||
AssetTest(tests[i]);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
#endif
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
|
|||
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
|
||||
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
|
||||
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
};
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags)
|
||||
|
|
|
|||
|
|
@ -1617,24 +1617,29 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!txdata.m_ready) {
|
||||
txdata.Init(tx);
|
||||
if (!txdata.m_spent_outputs_ready) {
|
||||
std::vector<CTxOut> spent_outputs;
|
||||
spent_outputs.reserve(tx.vin.size());
|
||||
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||
const COutPoint& prevout = tx.vin[i].prevout;
|
||||
// ELEMENTS:
|
||||
// If input is peg-in, create "coin" to evaluate against
|
||||
Coin pegin_coin;
|
||||
if (tx.vin[i].m_is_pegin) {
|
||||
// Height of "output" in script evaluation will be 0
|
||||
pegin_coin = Coin(GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness), 0, false);
|
||||
}
|
||||
const Coin& coin = tx.vin[i].m_is_pegin ? pegin_coin : inputs.AccessCoin(prevout);
|
||||
// end ELEMENTS
|
||||
assert(!coin.IsSpent());
|
||||
spent_outputs.emplace_back(coin.out);
|
||||
}
|
||||
txdata.Init(tx, std::move(spent_outputs));
|
||||
}
|
||||
assert(txdata.m_spent_outputs.size() == tx.vin.size());
|
||||
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
|
||||
// ELEMENTS:
|
||||
// If input is peg-in, create "coin" to evaluate against
|
||||
Coin pegin_coin;
|
||||
if (tx.vin[i].m_is_pegin) {
|
||||
// Height of "output" in script evaluation will be 0
|
||||
pegin_coin = Coin(GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness), 0, false);
|
||||
}
|
||||
|
||||
const Coin& coin = tx.vin[i].m_is_pegin ? pegin_coin : inputs.AccessCoin(prevout);
|
||||
assert(!coin.IsSpent());
|
||||
|
||||
// We very carefully only pass in things to CScriptCheck which
|
||||
// are clearly committed to by tx' witness hash. This provides
|
||||
// a sanity check that our caching is not introducing consensus
|
||||
|
|
@ -1642,7 +1647,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
|
|||
// spent being checked as a part of CScriptCheck.
|
||||
|
||||
// Verify signature
|
||||
CCheck* check = new CScriptCheck(coin.out, tx, i, flags, cacheSigStore, &txdata);
|
||||
CCheck* check = new CScriptCheck(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
|
||||
ScriptError serror = QueueCheck(pvChecks, check);
|
||||
if (serror != SCRIPT_ERR_OK) {
|
||||
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
|
||||
|
|
@ -1654,7 +1659,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
|
|||
// splitting the network between upgraded and
|
||||
// non-upgraded nodes by banning CONSENSUS-failing
|
||||
// data providers.
|
||||
CScriptCheck check2(coin.out, tx, i,
|
||||
CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
||||
if (check2()) {
|
||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(serror)));
|
||||
|
|
@ -2032,6 +2037,11 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
|
|||
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
|
||||
}
|
||||
|
||||
// Start enforcing Taproot using versionbits logic.
|
||||
if (VersionBitsState(pindex->pprev, consensusparams, Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE) {
|
||||
flags |= SCRIPT_VERIFY_TAPROOT;
|
||||
}
|
||||
|
||||
// Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit)
|
||||
if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
|
||||
flags |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
|
|||
/*.name =*/ "testdummy",
|
||||
/*.gbt_force =*/ true,
|
||||
},
|
||||
{
|
||||
/*.name =*/ "taproot",
|
||||
/*.gbt_force =*/ true,
|
||||
},
|
||||
{
|
||||
/*.name =*/ "dynafed",
|
||||
/*.gbt_force =*/ true,
|
||||
|
|
|
|||
|
|
@ -939,6 +939,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
|
|||
return "unspendable script";
|
||||
case TxoutType::NONSTANDARD:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
default:
|
||||
return "unrecognized script";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
|
|||
case TxoutType::NONSTANDARD:
|
||||
case TxoutType::NULL_DATA:
|
||||
case TxoutType::WITNESS_UNKNOWN:
|
||||
case TxoutType::WITNESS_V1_TAPROOT:
|
||||
case TxoutType::FEE:
|
||||
break;
|
||||
case TxoutType::PUBKEY:
|
||||
|
|
|
|||
1472
test/functional/feature_taproot.py
Executable file
1472
test/functional/feature_taproot.py
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -57,6 +57,7 @@ from test_framework.script import (
|
|||
MAX_SCRIPT_ELEMENT_SIZE,
|
||||
OP_0,
|
||||
OP_1,
|
||||
OP_2,
|
||||
OP_16,
|
||||
OP_2DROP,
|
||||
OP_CHECKMULTISIG,
|
||||
|
|
@ -1440,7 +1441,11 @@ class SegWitTest(BitcoinTestFramework):
|
|||
assert_equal(len(self.nodes[1].getrawmempool()), 0)
|
||||
for version in list(range(OP_1, OP_16 + 1)) + [OP_0]:
|
||||
# First try to spend to a future version segwit script_pubkey.
|
||||
script_pubkey = CScript([CScriptOp(version), witness_hash])
|
||||
if version == OP_1:
|
||||
# Don't use 32-byte v1 witness (used by Taproot; see BIP 341)
|
||||
script_pubkey = CScript([CScriptOp(version), witness_hash + b'\x00'])
|
||||
else:
|
||||
script_pubkey = CScript([CScriptOp(version), witness_hash])
|
||||
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")]
|
||||
tx.vout = [CTxOut(self.utxo[0].nValue - 1000, script_pubkey)]
|
||||
tx.vout.append(CTxOut(1000)) # fee
|
||||
|
|
@ -1454,9 +1459,9 @@ class SegWitTest(BitcoinTestFramework):
|
|||
self.sync_blocks()
|
||||
assert len(self.nodes[0].getrawmempool()) == 0
|
||||
|
||||
# Finally, verify that version 0 -> version 1 transactions
|
||||
# Finally, verify that version 0 -> version 2 transactions
|
||||
# are standard
|
||||
script_pubkey = CScript([CScriptOp(OP_1), witness_hash])
|
||||
script_pubkey = CScript([CScriptOp(OP_2), witness_hash])
|
||||
tx2 = CTransaction()
|
||||
tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
|
||||
tx2.vout = [CTxOut(tx.vout[0].nValue.getAmount() - 1000, script_pubkey)]
|
||||
|
|
|
|||
|
|
@ -155,7 +155,19 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
'possible': True,
|
||||
},
|
||||
},
|
||||
'active': False}
|
||||
'active': False
|
||||
},
|
||||
'taproot': {
|
||||
'type': 'bip9',
|
||||
'bip9': {
|
||||
'status': 'active',
|
||||
'start_time': -1,
|
||||
'timeout': 9223372036854775807,
|
||||
'since': 0
|
||||
},
|
||||
'height': 0,
|
||||
'active': True
|
||||
}
|
||||
})
|
||||
|
||||
def _test_getchaintxstats(self):
|
||||
|
|
|
|||
16
test/functional/test_framework/bip340_test_vectors.csv
Normal file
16
test/functional/test_framework/bip340_test_vectors.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
index,secret key,public key,aux_rand,message,signature,verification result,comment
|
||||
0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE,
|
||||
1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE,
|
||||
2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE,
|
||||
3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n
|
||||
4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE,
|
||||
5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve
|
||||
6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false
|
||||
7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message
|
||||
8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value
|
||||
9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0
|
||||
10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1
|
||||
11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve
|
||||
12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size
|
||||
13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
|
||||
14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size
|
||||
|
|
|
@ -45,7 +45,9 @@ from .script import (
|
|||
from .util import assert_equal
|
||||
from io import BytesIO
|
||||
|
||||
WITNESS_SCALE_FACTOR = 4
|
||||
MAX_BLOCK_SIGOPS = 20000
|
||||
MAX_BLOCK_SIGOPS_WEIGHT = MAX_BLOCK_SIGOPS * WITNESS_SCALE_FACTOR
|
||||
|
||||
# Genesis block time (regtest)
|
||||
TIME_GENESIS_BLOCK = 1296688602
|
||||
|
|
@ -118,23 +120,32 @@ def script_BIP34_coinbase_height(height):
|
|||
return CScript([CScriptNum(height)])
|
||||
|
||||
|
||||
def create_coinbase(height, pubkey=None):
|
||||
"""Create a coinbase transaction, assuming no miner fees.
|
||||
def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0):
|
||||
"""Create a coinbase transaction.
|
||||
|
||||
If pubkey is passed in, the coinbase output will be a P2PK output;
|
||||
otherwise an anyone-can-spend output."""
|
||||
otherwise an anyone-can-spend output.
|
||||
|
||||
If extra_output_script is given, make a 0-value output to that
|
||||
script. This is useful to pad block weight/sigops as needed. """
|
||||
coinbase = CTransaction()
|
||||
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), 0xffffffff))
|
||||
coinbaseoutput = CTxOut()
|
||||
value = 50 * COIN
|
||||
halvings = int(height / 150) # regtest
|
||||
value >>= halvings
|
||||
value += fees
|
||||
coinbaseoutput.nValue = CTxOutValue(value)
|
||||
if (pubkey is not None):
|
||||
coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG])
|
||||
else:
|
||||
coinbaseoutput.scriptPubKey = CScript([OP_TRUE])
|
||||
coinbase.vout = [coinbaseoutput]
|
||||
if extra_output_script is not None:
|
||||
coinbaseoutput2 = CTxOut()
|
||||
coinbaseoutput2.nValue = CTxOutValue(0)
|
||||
coinbaseoutput2.scriptPubKey = extra_output_script
|
||||
coinbase.vout.append(coinbaseoutput2)
|
||||
coinbase.calc_sha256()
|
||||
return coinbase
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2019 Pieter Wuille
|
||||
# Copyright (c) 2019-2020 Pieter Wuille
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test-only secp256k1 elliptic curve implementation
|
||||
|
|
@ -6,10 +6,24 @@
|
|||
WARNING: This code is slow, uses bad randomness, does not properly protect
|
||||
keys, and is trivially vulnerable to side channel attacks. Do not use for
|
||||
anything but tests."""
|
||||
import csv
|
||||
import hashlib
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from .util import modinv
|
||||
|
||||
def TaggedHash(tag, data):
|
||||
ss = hashlib.sha256(tag.encode('utf-8')).digest()
|
||||
ss += ss
|
||||
ss += data
|
||||
return hashlib.sha256(ss).digest()
|
||||
|
||||
def xor_bytes(b0, b1):
|
||||
return bytes(x ^ y for (x, y) in zip(b0, b1))
|
||||
|
||||
def jacobi_symbol(n, k):
|
||||
"""Compute the Jacobi symbol of n modulo k
|
||||
|
||||
|
|
@ -68,6 +82,10 @@ class EllipticCurve:
|
|||
inv_3 = (inv_2 * inv) % self.p
|
||||
return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
|
||||
|
||||
def has_even_y(self, p1):
|
||||
"""Whether the point p1 has an even Y coordinate when expressed in affine coordinates."""
|
||||
return not (p1[2] == 0 or self.affine(p1)[1] & 1)
|
||||
|
||||
def negate(self, p1):
|
||||
"""Negate a Jacobian point tuple p1."""
|
||||
x1, y1, z1 = p1
|
||||
|
|
@ -86,13 +104,13 @@ class EllipticCurve:
|
|||
return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
|
||||
|
||||
def lift_x(self, x):
|
||||
"""Given an X coordinate on the curve, return a corresponding affine point."""
|
||||
"""Given an X coordinate on the curve, return a corresponding affine point for which the Y coordinate is even."""
|
||||
x_3 = pow(x, 3, self.p)
|
||||
v = x_3 + self.a * x + self.b
|
||||
y = modsqrt(v, self.p)
|
||||
if y is None:
|
||||
return None
|
||||
return (x, y, 1)
|
||||
return (x, self.p - y if y & 1 else y, 1)
|
||||
|
||||
def double(self, p1):
|
||||
"""Double a Jacobian tuple p1
|
||||
|
|
@ -197,7 +215,8 @@ class EllipticCurve:
|
|||
r = self.add(r, p)
|
||||
return r
|
||||
|
||||
SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7)
|
||||
SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
|
||||
SECP256K1 = EllipticCurve(SECP256K1_FIELD_SIZE, 0, 7)
|
||||
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
|
||||
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
||||
|
|
@ -223,7 +242,7 @@ class ECPubKey():
|
|||
p = SECP256K1.lift_x(x)
|
||||
# if the oddness of the y co-ord isn't correct, find the other
|
||||
# valid y
|
||||
if (p[1] & 1) != (data[0] & 1):
|
||||
if data[0] & 1:
|
||||
p = SECP256K1.negate(p)
|
||||
self.p = p
|
||||
self.valid = True
|
||||
|
|
@ -307,6 +326,10 @@ class ECPubKey():
|
|||
return False
|
||||
return True
|
||||
|
||||
def generate_privkey():
|
||||
"""Generate a valid random 32-byte private key."""
|
||||
return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
|
||||
|
||||
class ECKey():
|
||||
"""A secp256k1 private key"""
|
||||
|
||||
|
|
@ -324,7 +347,7 @@ class ECKey():
|
|||
|
||||
def generate(self, compressed=True):
|
||||
"""Generate a random private key (compressed or uncompressed)."""
|
||||
self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
|
||||
self.set(generate_privkey(), compressed)
|
||||
|
||||
def get_bytes(self):
|
||||
"""Retrieve the 32-byte representation of this key."""
|
||||
|
|
@ -369,3 +392,161 @@ class ECKey():
|
|||
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
|
||||
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
|
||||
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
|
||||
|
||||
def compute_xonly_pubkey(key):
|
||||
"""Compute an x-only (32 byte) public key from a (32 byte) private key.
|
||||
|
||||
This also returns whether the resulting public key was negated.
|
||||
"""
|
||||
|
||||
assert len(key) == 32
|
||||
x = int.from_bytes(key, 'big')
|
||||
if x == 0 or x >= SECP256K1_ORDER:
|
||||
return (None, None)
|
||||
P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, x)]))
|
||||
return (P[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(P))
|
||||
|
||||
def tweak_add_privkey(key, tweak):
|
||||
"""Tweak a private key (after negating it if needed)."""
|
||||
|
||||
assert len(key) == 32
|
||||
assert len(tweak) == 32
|
||||
|
||||
x = int.from_bytes(key, 'big')
|
||||
if x == 0 or x >= SECP256K1_ORDER:
|
||||
return None
|
||||
if not SECP256K1.has_even_y(SECP256K1.mul([(SECP256K1_G, x)])):
|
||||
x = SECP256K1_ORDER - x
|
||||
t = int.from_bytes(tweak, 'big')
|
||||
if t >= SECP256K1_ORDER:
|
||||
return None
|
||||
x = (x + t) % SECP256K1_ORDER
|
||||
if x == 0:
|
||||
return None
|
||||
return x.to_bytes(32, 'big')
|
||||
|
||||
def tweak_add_pubkey(key, tweak):
|
||||
"""Tweak a public key and return whether the result had to be negated."""
|
||||
|
||||
assert len(key) == 32
|
||||
assert len(tweak) == 32
|
||||
|
||||
x_coord = int.from_bytes(key, 'big')
|
||||
if x_coord >= SECP256K1_FIELD_SIZE:
|
||||
return None
|
||||
P = SECP256K1.lift_x(x_coord)
|
||||
if P is None:
|
||||
return None
|
||||
t = int.from_bytes(tweak, 'big')
|
||||
if t >= SECP256K1_ORDER:
|
||||
return None
|
||||
Q = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, t), (P, 1)]))
|
||||
if Q is None:
|
||||
return None
|
||||
return (Q[0].to_bytes(32, 'big'), not SECP256K1.has_even_y(Q))
|
||||
|
||||
def verify_schnorr(key, sig, msg):
|
||||
"""Verify a Schnorr signature (see BIP 340).
|
||||
|
||||
- key is a 32-byte xonly pubkey (computed using compute_xonly_pubkey).
|
||||
- sig is a 64-byte Schnorr signature
|
||||
- msg is a 32-byte message
|
||||
"""
|
||||
assert len(key) == 32
|
||||
assert len(msg) == 32
|
||||
assert len(sig) == 64
|
||||
|
||||
x_coord = int.from_bytes(key, 'big')
|
||||
if x_coord == 0 or x_coord >= SECP256K1_FIELD_SIZE:
|
||||
return False
|
||||
P = SECP256K1.lift_x(x_coord)
|
||||
if P is None:
|
||||
return False
|
||||
r = int.from_bytes(sig[0:32], 'big')
|
||||
if r >= SECP256K1_FIELD_SIZE:
|
||||
return False
|
||||
s = int.from_bytes(sig[32:64], 'big')
|
||||
if s >= SECP256K1_ORDER:
|
||||
return False
|
||||
e = int.from_bytes(TaggedHash("BIP0340/challenge", sig[0:32] + key + msg), 'big') % SECP256K1_ORDER
|
||||
R = SECP256K1.mul([(SECP256K1_G, s), (P, SECP256K1_ORDER - e)])
|
||||
if not SECP256K1.has_even_y(R):
|
||||
return False
|
||||
if ((r * R[2] * R[2]) % SECP256K1_FIELD_SIZE) != R[0]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def sign_schnorr(key, msg, aux=None, flip_p=False, flip_r=False):
|
||||
"""Create a Schnorr signature (see BIP 340)."""
|
||||
|
||||
if aux is None:
|
||||
aux = bytes(32)
|
||||
|
||||
assert len(key) == 32
|
||||
assert len(msg) == 32
|
||||
assert len(aux) == 32
|
||||
|
||||
sec = int.from_bytes(key, 'big')
|
||||
if sec == 0 or sec >= SECP256K1_ORDER:
|
||||
return None
|
||||
P = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, sec)]))
|
||||
if SECP256K1.has_even_y(P) == flip_p:
|
||||
sec = SECP256K1_ORDER - sec
|
||||
t = (sec ^ int.from_bytes(TaggedHash("BIP0340/aux", aux), 'big')).to_bytes(32, 'big')
|
||||
kp = int.from_bytes(TaggedHash("BIP0340/nonce", t + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
|
||||
assert kp != 0
|
||||
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, kp)]))
|
||||
k = kp if SECP256K1.has_even_y(R) != flip_r else SECP256K1_ORDER - kp
|
||||
e = int.from_bytes(TaggedHash("BIP0340/challenge", R[0].to_bytes(32, 'big') + P[0].to_bytes(32, 'big') + msg), 'big') % SECP256K1_ORDER
|
||||
return R[0].to_bytes(32, 'big') + ((k + e * sec) % SECP256K1_ORDER).to_bytes(32, 'big')
|
||||
|
||||
class TestFrameworkKey(unittest.TestCase):
|
||||
def test_schnorr(self):
|
||||
"""Test the Python Schnorr implementation."""
|
||||
byte_arrays = [generate_privkey() for _ in range(3)] + [v.to_bytes(32, 'big') for v in [0, SECP256K1_ORDER - 1, SECP256K1_ORDER, 2**256 - 1]]
|
||||
keys = {}
|
||||
for privkey in byte_arrays: # build array of key/pubkey pairs
|
||||
pubkey, _ = compute_xonly_pubkey(privkey)
|
||||
if pubkey is not None:
|
||||
keys[privkey] = pubkey
|
||||
for msg in byte_arrays: # test every combination of message, signing key, verification key
|
||||
for sign_privkey, sign_pubkey in keys.items():
|
||||
sig = sign_schnorr(sign_privkey, msg)
|
||||
for verify_privkey, verify_pubkey in keys.items():
|
||||
if verify_privkey == sign_privkey:
|
||||
self.assertTrue(verify_schnorr(verify_pubkey, sig, msg))
|
||||
sig = list(sig)
|
||||
sig[random.randrange(64)] ^= (1 << (random.randrange(8))) # damaging signature should break things
|
||||
sig = bytes(sig)
|
||||
self.assertFalse(verify_schnorr(verify_pubkey, sig, msg))
|
||||
|
||||
def test_schnorr_testvectors(self):
|
||||
"""Implement the BIP340 test vectors (read from bip340_test_vectors.csv)."""
|
||||
num_tests = 0
|
||||
with open(os.path.join(sys.path[0], 'test_framework', 'bip340_test_vectors.csv'), newline='', encoding='utf8') as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
next(reader)
|
||||
for row in reader:
|
||||
(i_str, seckey_hex, pubkey_hex, aux_rand_hex, msg_hex, sig_hex, result_str, comment) = row
|
||||
i = int(i_str)
|
||||
pubkey = bytes.fromhex(pubkey_hex)
|
||||
msg = bytes.fromhex(msg_hex)
|
||||
sig = bytes.fromhex(sig_hex)
|
||||
result = result_str == 'TRUE'
|
||||
if seckey_hex != '':
|
||||
seckey = bytes.fromhex(seckey_hex)
|
||||
pubkey_actual = compute_xonly_pubkey(seckey)[0]
|
||||
self.assertEqual(pubkey.hex(), pubkey_actual.hex(), "BIP340 test vector %i (%s): pubkey mismatch" % (i, comment))
|
||||
aux_rand = bytes.fromhex(aux_rand_hex)
|
||||
try:
|
||||
sig_actual = sign_schnorr(seckey, msg, aux_rand)
|
||||
self.assertEqual(sig.hex(), sig_actual.hex(), "BIP340 test vector %i (%s): sig mismatch" % (i, comment))
|
||||
except RuntimeError as e:
|
||||
self.assertFalse("BIP340 test vector %i (%s): signing raised exception %s" % (i, comment, e))
|
||||
result_actual = verify_schnorr(pubkey, sig, msg)
|
||||
if result:
|
||||
self.assertEqual(result, result_actual, "BIP340 test vector %i (%s): verification failed" % (i, comment))
|
||||
else:
|
||||
self.assertEqual(result, result_actual, "BIP340 test vector %i (%s): verification succeeded unexpectedly" % (i, comment))
|
||||
num_tests += 1
|
||||
self.assertTrue(num_tests >= 15) # expect at least 15 test vectors
|
||||
|
|
|
|||
|
|
@ -6,14 +6,20 @@
|
|||
|
||||
This file is modified from python-bitcoinlib.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
import hashlib
|
||||
import struct
|
||||
import unittest
|
||||
from typing import List, Dict
|
||||
|
||||
from .key import TaggedHash, tweak_add_pubkey
|
||||
|
||||
from .messages import (
|
||||
CTransaction,
|
||||
CTxOut,
|
||||
CTxOutAsset,
|
||||
CTxOutValue,
|
||||
hash256,
|
||||
ser_string,
|
||||
ser_uint256,
|
||||
|
|
@ -23,8 +29,13 @@ from .messages import (
|
|||
)
|
||||
|
||||
MAX_SCRIPT_ELEMENT_SIZE = 520
|
||||
LOCKTIME_THRESHOLD = 500000000
|
||||
ANNEX_TAG = 0x50
|
||||
|
||||
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
|
||||
|
||||
LEAF_VERSION_TAPSCRIPT = 0xc0
|
||||
|
||||
def hash160(s):
|
||||
return hashlib.new('ripemd160', sha256(s)).digest()
|
||||
|
||||
|
|
@ -240,11 +251,8 @@ OP_NOP8 = CScriptOp(0xb7)
|
|||
OP_NOP9 = CScriptOp(0xb8)
|
||||
OP_NOP10 = CScriptOp(0xb9)
|
||||
|
||||
# template matching params
|
||||
OP_SMALLINTEGER = CScriptOp(0xfa)
|
||||
OP_PUBKEYS = CScriptOp(0xfb)
|
||||
OP_PUBKEYHASH = CScriptOp(0xfd)
|
||||
OP_PUBKEY = CScriptOp(0xfe)
|
||||
# BIP 342 opcodes (Tapscript)
|
||||
OP_CHECKSIGADD = CScriptOp(0xba)
|
||||
|
||||
OP_INVALIDOPCODE = CScriptOp(0xff)
|
||||
|
||||
|
|
@ -360,10 +368,7 @@ OPCODE_NAMES.update({
|
|||
OP_NOP8: 'OP_NOP8',
|
||||
OP_NOP9: 'OP_NOP9',
|
||||
OP_NOP10: 'OP_NOP10',
|
||||
OP_SMALLINTEGER: 'OP_SMALLINTEGER',
|
||||
OP_PUBKEYS: 'OP_PUBKEYS',
|
||||
OP_PUBKEYHASH: 'OP_PUBKEYHASH',
|
||||
OP_PUBKEY: 'OP_PUBKEY',
|
||||
OP_CHECKSIGADD: 'OP_CHECKSIGADD',
|
||||
OP_INVALIDOPCODE: 'OP_INVALIDOPCODE',
|
||||
})
|
||||
|
||||
|
|
@ -594,6 +599,7 @@ class CScript(bytes):
|
|||
return n
|
||||
|
||||
|
||||
SIGHASH_DEFAULT = 0 # Taproot-only default, semantics same as SIGHASH_ALL
|
||||
SIGHASH_ALL = 1
|
||||
SIGHASH_NONE = 2
|
||||
SIGHASH_SINGLE = 3
|
||||
|
|
@ -616,7 +622,6 @@ def FindAndDelete(script, sig):
|
|||
r += script[last_sop_idx:]
|
||||
return CScript(r)
|
||||
|
||||
|
||||
def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
||||
"""Consensus-correct SignatureHash
|
||||
|
||||
|
|
@ -648,7 +653,7 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
|||
tmp = txtmp.vout[outIdx]
|
||||
txtmp.vout = []
|
||||
for _ in range(outIdx):
|
||||
txtmp.vout.append(CTxOut(-1))
|
||||
txtmp.vout.append(CTxOut(nValue=CTxOutValue(), nAsset=CTxOutAsset()))
|
||||
txtmp.vout.append(tmp)
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
|
|
@ -756,3 +761,116 @@ class TestFrameworkScript(unittest.TestCase):
|
|||
values = [0, 1, -1, -2, 127, 128, -255, 256, (1 << 15) - 1, -(1 << 16), (1 << 24) - 1, (1 << 31), 1 - (1 << 32), 1 << 40, 1500, -1500]
|
||||
for value in values:
|
||||
self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
|
||||
|
||||
def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
|
||||
assert (len(txTo.vin) == len(spent_utxos))
|
||||
assert (input_index < len(txTo.vin))
|
||||
out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
|
||||
in_type = hash_type & SIGHASH_ANYONECANPAY
|
||||
spk = spent_utxos[input_index].scriptPubKey
|
||||
ss = bytes([0, hash_type]) # epoch, hash_type
|
||||
ss += struct.pack("<i", txTo.nVersion)
|
||||
ss += struct.pack("<I", txTo.nLockTime)
|
||||
if in_type != SIGHASH_ANYONECANPAY:
|
||||
ss += sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
|
||||
ss += sha256(b"".join(u.nValue.serialize() for u in spent_utxos))
|
||||
ss += sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
|
||||
ss += sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
|
||||
if out_type == SIGHASH_ALL:
|
||||
ss += sha256(b"".join(o.serialize() for o in txTo.vout))
|
||||
spend_type = 0
|
||||
if annex is not None:
|
||||
spend_type |= 1
|
||||
if (scriptpath):
|
||||
spend_type |= 2
|
||||
ss += bytes([spend_type])
|
||||
if in_type == SIGHASH_ANYONECANPAY:
|
||||
ss += txTo.vin[input_index].prevout.serialize()
|
||||
ss += spent_utxos[input_index].nAsset.serialize()
|
||||
ss += spent_utxos[input_index].nValue.serialize()
|
||||
ss += spent_utxos[input_index].nNonce.serialize()
|
||||
ss += ser_string(spk)
|
||||
ss += struct.pack("<I", txTo.vin[input_index].nSequence)
|
||||
else:
|
||||
ss += struct.pack("<I", input_index)
|
||||
if (spend_type & 1):
|
||||
ss += sha256(ser_string(annex))
|
||||
if out_type == SIGHASH_SINGLE:
|
||||
if input_index < len(txTo.vout):
|
||||
ss += sha256(txTo.vout[input_index].serialize())
|
||||
else:
|
||||
ss += bytes(0 for _ in range(32))
|
||||
if (scriptpath):
|
||||
ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
|
||||
ss += bytes([0])
|
||||
ss += struct.pack("<i", codeseparator_pos)
|
||||
# ELEMENTS -35 since we encode nAsset (33) + nValue (9) + nNonce (1) rather than nValue (8)
|
||||
assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * (49 - 35) - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
|
||||
return TaggedHash("TapSighash", ss)
|
||||
|
||||
def taproot_tree_helper(scripts):
|
||||
if len(scripts) == 0:
|
||||
return ([], bytes(0 for _ in range(32)))
|
||||
if len(scripts) == 1:
|
||||
# One entry: treat as a leaf
|
||||
script = scripts[0]
|
||||
assert(not callable(script))
|
||||
if isinstance(script, list):
|
||||
return taproot_tree_helper(script)
|
||||
assert(isinstance(script, tuple))
|
||||
version = LEAF_VERSION_TAPSCRIPT
|
||||
name = script[0]
|
||||
code = script[1]
|
||||
if len(script) == 3:
|
||||
version = script[2]
|
||||
assert version & 1 == 0
|
||||
assert isinstance(code, bytes)
|
||||
h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
|
||||
if name is None:
|
||||
return ([], h)
|
||||
return ([(name, version, code, bytes())], h)
|
||||
elif len(scripts) == 2 and callable(scripts[1]):
|
||||
# Two entries, and the right one is a function
|
||||
left, left_h = taproot_tree_helper(scripts[0:1])
|
||||
right_h = scripts[1](left_h)
|
||||
left = [(name, version, script, control + right_h) for name, version, script, control in left]
|
||||
right = []
|
||||
else:
|
||||
# Two or more entries: descend into each side
|
||||
split_pos = len(scripts) // 2
|
||||
left, left_h = taproot_tree_helper(scripts[0:split_pos])
|
||||
right, right_h = taproot_tree_helper(scripts[split_pos:])
|
||||
left = [(name, version, script, control + right_h) for name, version, script, control in left]
|
||||
right = [(name, version, script, control + left_h) for name, version, script, control in right]
|
||||
if right_h < left_h:
|
||||
right_h, left_h = left_h, right_h
|
||||
h = TaggedHash("TapBranch", left_h + right_h)
|
||||
return (left + right, h)
|
||||
|
||||
TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves")
|
||||
TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch")
|
||||
|
||||
def taproot_construct(pubkey, scripts=None):
|
||||
"""Construct a tree of Taproot spending conditions
|
||||
|
||||
pubkey: an ECPubKey object for the internal pubkey
|
||||
scripts: a list of items; each item is either:
|
||||
- a (name, CScript) tuple
|
||||
- a (name, CScript, leaf version) tuple
|
||||
- another list of items (with the same structure)
|
||||
- a function, which specifies how to compute the hashing partner
|
||||
in function of the hash of whatever it is combined with
|
||||
|
||||
Returns: script (sPK or redeemScript), tweak, {name:(script, leaf version, negation flag, innerkey, merklepath), ...}
|
||||
"""
|
||||
if scripts is None:
|
||||
scripts = []
|
||||
|
||||
ret, h = taproot_tree_helper(scripts)
|
||||
tweak = TaggedHash("TapTweak", pubkey + h)
|
||||
tweaked, negated = tweak_add_pubkey(pubkey, tweak)
|
||||
leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret)
|
||||
return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves)
|
||||
|
||||
def is_op_success(o):
|
||||
return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ TEST_FRAMEWORK_MODULES = [
|
|||
"address",
|
||||
"blocktools",
|
||||
"muhash",
|
||||
"key",
|
||||
"script",
|
||||
"segwit_addr",
|
||||
"util",
|
||||
|
|
@ -129,6 +130,7 @@ BASE_SCRIPTS = [
|
|||
'mempool_updatefromblock.py',
|
||||
'wallet_dump.py',
|
||||
'wallet_listtransactions.py',
|
||||
'feature_taproot.py',
|
||||
# vv Tests less than 60s vv
|
||||
'p2p_sendheaders.py',
|
||||
'wallet_importmulti.py',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue