diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 2fe48c88b4e78..ae5622101814d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -283,6 +283,7 @@ test_fuzz_fuzz_SOURCES = \ $(FUZZ_WALLET_SRC) \ test/fuzz/addition_overflow.cpp \ test/fuzz/addrman.cpp \ + test/fuzz/anyprevout.cpp \ test/fuzz/asmap.cpp \ test/fuzz/asmap_direct.cpp \ test/fuzz/autofile.cpp \ diff --git a/src/consensus/params.h b/src/consensus/params.h index 6cb3ab6123e60..4566a47d7a8d8 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -33,6 +33,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_ enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_CHECKTEMPLATEVERIFY, // Deployment of CTV (BIP 119) + DEPLOYMENT_ANYPREVOUT, // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index f5492ac3bdef6..e75425ab7f028 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -19,6 +19,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/ "checktemplateverify", /*.gbt_force =*/ true, }, + { + /*.name =*/ "anyprevout", + /*.gbt_force =*/ true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) @@ -83,6 +87,8 @@ const std::map g_verify_flag_names{ FLAG_NAME(DEFAULT_CHECK_TEMPLATE_VERIFY_HASH), FLAG_NAME(DISCOURAGE_UPGRADABLE_CHECK_TEMPLATE_VERIFY_HASH), FLAG_NAME(DISCOURAGE_CHECK_TEMPLATE_VERIFY_HASH), + FLAG_NAME(ANYPREVOUT), + FLAG_NAME(DISCOURAGE_ANYPREVOUT), }; #undef FLAG_NAME diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 4f60a2979ba8a..9b874e8cbab80 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -132,6 +132,7 @@ class CMainParams : public CChainParams { consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = SetupDeployment{.activate = 0x30000000, .abandon = 0, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .never = true}; + consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .never = true}; consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000063c4ebd298db40af57541800"); consensus.defaultAssumeValid = uint256S("0x000000000000000000026811d149d4d261995ec5b3f64f439a0a10e1a464af9a"); // 824000 @@ -243,6 +244,7 @@ class CTestNetParams : public CChainParams { consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = SetupDeployment{.activate = 0x30000000, .abandon = 0, .never = true}; consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .never = true}; + consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .never = true}; consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000c59b14e264ba6c15db9"); consensus.defaultAssumeValid = uint256S("0x000000000001323071f38f21ea5aae529ece491eadaccce506a59bcc2d968917"); // 2550000 @@ -378,6 +380,12 @@ class SigNetParams : public CChainParams { .activate = 0x60007700, .abandon = 0x40007700, }; + consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{ + .start = 1625875200, // 2021-07-10 + .timeout = 1941408000, // 2031-07-10 + .activate = 0x60007600, + .abandon = 0x40007600, + }; RenounceDeployments(options.renounce, consensus.vDeployments); @@ -451,6 +459,7 @@ class CRegTestParams : public CChainParams // 0x3000_0000 = bit 28 plus versionbits signalling; 0x5000_0000 = bit 38 plus VERSIONBITS_TOP_ABANDON consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = SetupDeployment{.start = 0, .timeout = Consensus::HereticalDeployment::NO_TIMEOUT, .activate = 0x30000000, .abandon = 0x50000000}; consensus.vDeployments[Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY] = SetupDeployment{.activate = 0x60007700, .abandon = 0x40007700, .always = true}; + consensus.vDeployments[Consensus::DEPLOYMENT_ANYPREVOUT] = SetupDeployment{.activate = 0x60007600, .abandon = 0x40007600, .always = true}; consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; diff --git a/src/policy/policy.h b/src/policy/policy.h index 6b253639e3de6..2b047ae9d208b 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -115,7 +115,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECK_TEMPLATE_VERIFY_HASH | - SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH}; + SCRIPT_VERIFY_DEFAULT_CHECK_TEMPLATE_VERIFY_HASH | + SCRIPT_VERIFY_ANYPREVOUT}; /** 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}; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 02048c2a59ae2..1295ff26101db 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1332,6 +1332,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_CHECKTEMPLATEVERIFY); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_ANYPREVOUT); return softforks; } } // anon namespace diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 9ae7164fe83e8..7497ae8df10ae 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -346,6 +346,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu 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); + assert(execdata.m_internal_key); // caller must provide the internal key /* * The following validation sequence is consensus critical. Please note how -- @@ -363,12 +364,27 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr 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)) { + if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::TAPROOT, pubkey, sigversion, execdata, serror)) { return false; // serror is set } + } else if ((pubkey.size() == 1 || pubkey.size() == 33) && pubkey[0] == BIP118_PUBKEY_PREFIX) { + if ((flags & SCRIPT_VERIFY_DISCOURAGE_ANYPREVOUT) != 0) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_ANYPREVOUT); + } else if ((flags & SCRIPT_VERIFY_ANYPREVOUT) == 0) { + return true; + } else if (pubkey.size() == 1) { + if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::ANYPREVOUT, *execdata.m_internal_key, sigversion, execdata, serror)) { + return false; // serror is set + } + } else { // pubkey.size() == 33 + if (success && !checker.CheckSchnorrSignature(sig, KeyVersion::ANYPREVOUT, Span(pubkey).subspan(1), sigversion, execdata, serror)) { + return false; // serror is set + } + } } else { /* * New public key version softforks should be defined before this `else` block. @@ -1583,21 +1599,20 @@ static bool HandleMissingData(MissingDataBehavior mdb) } template -bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb) +bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, KeyVersion keyversion, const PrecomputedTransactionData& cache, MissingDataBehavior mdb) { - uint8_t ext_flag, key_version; + uint8_t ext_flag; + assert( + (keyversion == KeyVersion::TAPROOT) || + (keyversion == KeyVersion::ANYPREVOUT && sigversion == SigVersion::TAPSCRIPT) + ); switch (sigversion) { case SigVersion::TAPROOT: ext_flag = 0; - // key_version is not used and left uninitialized. + // keyversion is not used. 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); @@ -1616,13 +1631,27 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons // 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; + + switch(hash_type) { + case 0: case 1: case 2: case 3: + case 0x81: case 0x82: case 0x83: + break; + case 0x41: case 0x42: case 0x43: + case 0xc1: case 0xc2: case 0xc3: + if (keyversion == KeyVersion::ANYPREVOUT) { + break; + } else { + return false; + } + default: + return false; + } ss << hash_type; // Transaction level data ss << tx_to.nVersion; ss << tx_to.nLockTime; - if (input_type != SIGHASH_ANYONECANPAY) { + if (input_type != SIGHASH_ANYONECANPAY && input_type != SIGHASH_ANYPREVOUT && input_type != SIGHASH_ANYPREVOUTANYSCRIPT) { ss << cache.m_prevouts_single_hash; ss << cache.m_spent_amounts_single_hash; ss << cache.m_spent_scripts_single_hash; @@ -1641,6 +1670,13 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons ss << tx_to.vin[in_pos].prevout; ss << cache.m_spent_outputs[in_pos]; ss << tx_to.vin[in_pos].nSequence; + } else if (input_type == SIGHASH_ANYPREVOUT) { + assert(keyversion == KeyVersion::ANYPREVOUT); + ss << cache.m_spent_outputs[in_pos]; + ss << tx_to.vin[in_pos].nSequence; + } else if (input_type == SIGHASH_ANYPREVOUTANYSCRIPT) { + assert(keyversion == KeyVersion::ANYPREVOUT); + ss << tx_to.vin[in_pos].nSequence; } else { ss << in_pos; } @@ -1662,8 +1698,12 @@ bool SignatureHashSchnorr(uint256& hash_out, ScriptExecutionData& execdata, cons // Additional data for BIP 342 signatures if (sigversion == SigVersion::TAPSCRIPT) { assert(execdata.m_tapleaf_hash_init); - ss << execdata.m_tapleaf_hash; - ss << key_version; + if (input_type != SIGHASH_ANYPREVOUTANYSCRIPT) { + ss << execdata.m_tapleaf_hash; + } else { + assert(keyversion == KeyVersion::ANYPREVOUT); + } + ss << uint8_t(keyversion); assert(execdata.m_codeseparator_pos_init); ss << execdata.m_codeseparator_pos; } @@ -1778,18 +1818,19 @@ bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vecto } template -bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, Span pubkey_in, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror) const +bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, KeyVersion pubkeyver, Span pubkey, SigVersion sigversion, 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); + assert(pubkey.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}; + XOnlyPubKey pubkey_xonly{pubkey}; uint8_t hashtype = SIGHASH_DEFAULT; if (sig.size() == 65) { @@ -1798,10 +1839,10 @@ bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Spantxdata) return HandleMissingData(m_mdb); - if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, *this->txdata, m_mdb)) { + if (!SignatureHashSchnorr(sighash, execdata, *txTo, nIn, hashtype, sigversion, pubkeyver, *this->txdata, m_mdb)) { return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE); } - if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG); + if (!VerifySchnorrSignature(sig, pubkey_xonly, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG); return true; } @@ -1980,12 +2021,13 @@ uint256 ComputeTaprootMerkleRoot(Span control, const uint25 return k; } -static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash) +static bool VerifyTaprootCommitment(const std::vector& control, const std::vector& program, const uint256& tapleaf_hash, std::optional& internal_key) { assert(control.size() >= TAPROOT_CONTROL_BASE_SIZE); assert(program.size() >= uint256::size()); //! The internal pubkey (x-only, so no Y coordinate parity). const XOnlyPubKey p{Span{control}.subspan(1, TAPROOT_CONTROL_BASE_SIZE - 1)}; + internal_key = p; //! The output pubkey (taken from the scriptPubKey). const XOnlyPubKey q{program}; // Compute the Merkle root from the leaf and the provided path. @@ -2039,7 +2081,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, 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)) { + if (!checker.CheckSchnorrSignature(stack.front(), KeyVersion::TAPROOT, program, SigVersion::TAPROOT, execdata, serror)) { return false; // serror is set } return set_success(serror); @@ -2051,7 +2093,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE); } execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script); - if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash)) { + if (!VerifyTaprootCommitment(control, program, execdata.m_tapleaf_hash, execdata.m_internal_key)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } execdata.m_tapleaf_hash_init = true; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index a067a6c1fd330..493b52af3f894 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -9,6 +9,7 @@ #include #include #include +#include #include