aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/chainparams.cpp13
-rw-r--r--src/consensus/params.h1
-rw-r--r--src/hash.cpp10
-rw-r--r--src/hash.h9
-rw-r--r--src/policy/policy.cpp34
-rw-r--r--src/policy/policy.h8
-rw-r--r--src/pubkey.cpp22
-rw-r--r--src/pubkey.h24
-rw-r--r--src/rpc/blockchain.cpp1
-rw-r--r--src/script/interpreter.cpp467
-rw-r--r--src/script/interpreter.h98
-rw-r--r--src/script/script.cpp11
-rw-r--r--src/script/script.h17
-rw-r--r--src/script/script_error.cpp20
-rw-r--r--src/script/script_error.h12
-rw-r--r--src/script/sigcache.cpp43
-rw-r--r--src/script/sigcache.h4
-rw-r--r--src/script/sign.cpp9
-rw-r--r--src/script/sign.h1
-rw-r--r--src/script/standard.cpp8
-rw-r--r--src/script/standard.h4
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp200
-rw-r--r--src/test/fuzz/script_sigcache.cpp18
-rw-r--r--src/test/fuzz/signature_checker.cpp7
-rw-r--r--src/test/key_tests.cpp28
-rw-r--r--src/test/script_tests.cpp138
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/validation.cpp26
-rw-r--r--src/versionbitsinfo.cpp4
-rw-r--r--src/wallet/rpcdump.cpp1
-rw-r--r--src/wallet/scriptpubkeyman.cpp1
32 files changed, 1157 insertions, 90 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 835b12ce3e..7fac78f973 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -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 \
@@ -1083,6 +1084,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)
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index ef501e9de2..a34bf350fc 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -86,6 +86,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");
@@ -197,6 +202,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");
@@ -380,6 +390,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");
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 85ab3f61ef..932f0d2c60 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -14,6 +14,7 @@ namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
+ DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
diff --git a/src/hash.cpp b/src/hash.cpp
index 83b90ae063..3657b38639 100644
--- a/src/hash.cpp
+++ b/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;
+}
diff --git a/src/hash.h b/src/hash.h
index c16bbb48ce..6d876076ee 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -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
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 0e9820da1e..69f2b456f1 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -9,7 +9,7 @@
#include <consensus/validation.h>
#include <coins.h>
-
+#include <span.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
@@ -206,6 +206,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
@@ -216,6 +217,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;
@@ -237,6 +239,36 @@ 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) {
+ // Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
+ auto stack = MakeSpan(tx.vin[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;
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 7f168ee20f..51d67b9390 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -40,6 +40,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
@@ -68,7 +70,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;
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index fc14f41a0c..4d734fc891 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -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;
diff --git a/src/pubkey.h b/src/pubkey.h
index fcbc7e8416..0f784b86e4 100644
--- a/src/pubkey.h
+++ b/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) 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];
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 1fbddc1ceb..a162c1ee70 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1354,6 +1354,7 @@ RPCHelpMan getblockchaininfo()
BuriedForkDescPushBack(softforks, "csv", consensusParams.CSVHeight);
BuriedForkDescPushBack(softforks, "segwit", consensusParams.SegwitHeight);
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
+ BIP9SoftForkDescPushBack(softforks, "taproot", consensusParams, Consensus::DEPLOYMENT_TAPROOT);
obj.pushKV("softforks", softforks);
obj.pushKV("warnings", GetWarnings(false).original);
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 50a6192476..5735e7df66 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -342,13 +342,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);
@@ -363,7 +360,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);
@@ -371,7 +368,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);
@@ -381,6 +438,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();
@@ -389,15 +449,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();
//
@@ -408,9 +471,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);
+ }
+ }
if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
@@ -568,6 +634,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);
@@ -1001,6 +1076,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;
@@ -1015,7 +1091,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);
@@ -1029,9 +1105,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;
@@ -1089,7 +1188,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++;
@@ -1159,6 +1258,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 {
/**
@@ -1291,35 +1396,183 @@ 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));
- 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 (!txTo.vin[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);
+ 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 CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
@@ -1329,7 +1582,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts;
uint256 hashSequence;
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));
@@ -1389,13 +1642,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())
@@ -1410,13 +1669,41 @@ 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
{
// There are two kinds of nLockTime: lock-by-blockheight
@@ -1504,17 +1791,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);
@@ -1522,40 +1831,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.
@@ -1601,7 +1974,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
@@ -1646,7 +2019,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
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 71f2436369..c0c2b012c6 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -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
@@ -114,19 +125,49 @@ enum
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
//
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),
};
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;
- 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);
@@ -134,13 +175,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 CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
@@ -148,7 +224,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;
}
@@ -176,12 +257,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 CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& 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;
};
@@ -189,6 +272,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);
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 92c6fe7785..f31472e42d 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -140,6 +140,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:
@@ -328,3 +331,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);
+}
diff --git a/src/script/script.h b/src/script/script.h
index c1f2b66921..974cde4984 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -44,6 +44,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;
+
template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
@@ -187,6 +198,9 @@ enum opcodetype
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
+ // Opcode added by BIP 342 (Tapscript)
+ OP_CHECKSIGADD = 0xba,
+
OP_INVALIDOPCODE = 0xff,
};
@@ -555,4 +569,7 @@ struct CScriptWitness
std::string ToString() const;
};
+/** Test for OP_SUCCESSx opcodes as defined by BIP342. */
+bool IsOpSuccess(const opcodetype& opcode);
+
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 3b383393f9..fadc04262c 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -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:
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 2978c147e1..b071681613 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -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,
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index aaecab1ef2..4a6e04f2eb 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -22,8 +22,9 @@ 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;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
@@ -34,18 +35,30 @@ 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'};
+ 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);
}
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());
+ }
+
bool
Get(const uint256& entry, const bool erase)
{
@@ -85,15 +98,25 @@ 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;
+}
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 807b61b542..00534f9758 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -7,6 +7,7 @@
#define BITCOIN_SCRIPT_SIGCACHE_H
#include <script/interpreter.h>
+#include <span.h>
#include <vector>
@@ -48,7 +49,8 @@ private:
public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& 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();
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 9b3f94f14d..0e6864d547 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -111,6 +111,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;
@@ -260,9 +261,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;
@@ -339,7 +340,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;
}
@@ -400,7 +401,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;
diff --git a/src/script/sign.h b/src/script/sign.h
index b77d26c0d7..a1cfe1574d 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -11,6 +11,7 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/keyorigin.h>
+#include <span.h>
#include <streams.h>
class CKey;
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 96a3d311a6..f2f81664f6 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -55,6 +55,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";
} // no default case, so the compiler can warn about missing cases
assert(false);
@@ -130,6 +131,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));
@@ -203,7 +209,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);
diff --git a/src/script/standard.h b/src/script/standard.h
index 6dbcd04968..721203385e 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -129,6 +129,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
};
@@ -206,7 +207,8 @@ struct WitnessUnknown
* * 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)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
new file mode 100644
index 0000000000..d20fa43d68
--- /dev/null
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -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&) {}
+}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 434a47b702..87af71897b 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -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));
+ }
}
}
}
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index 3aaeb66649..e121c89665 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -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();
}
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 4e4c44266a..3362b8d17c 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -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()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 0830743d61..a2efd8ac07 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -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>
@@ -1339,13 +1341,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)
{
@@ -1610,5 +1640,107 @@ 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());
+ 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.vin[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.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ BOOST_CHECK(ret);
+ }
+ }
+ }
+
+ if (test.exists("failure")) {
+ mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
+ mtx.vin[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.vin[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()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 94b5dba913..b7ee280336 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -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)
diff --git a/src/validation.cpp b/src/validation.cpp
index 06b9b36fca..423b93479a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1532,14 +1532,21 @@ 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 (const auto& txin : tx.vin) {
+ const COutPoint& prevout = txin.prevout;
+ const Coin& coin = inputs.AccessCoin(prevout);
+ 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;
- const Coin& 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
@@ -1548,7 +1555,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// spent being checked as a part of CScriptCheck.
// Verify signature
- CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
+ CScriptCheck check(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@@ -1562,7 +1569,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(check.GetScriptError())));
@@ -1907,6 +1914,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;
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
index 20297b9f9d..20dfc044ca 100644
--- a/src/versionbitsinfo.cpp
+++ b/src/versionbitsinfo.cpp
@@ -11,4 +11,8 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "testdummy",
/*.gbt_force =*/ true,
},
+ {
+ /*.name =*/ "taproot",
+ /*.gbt_force =*/ true,
+ },
};
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 9e36a09780..7dcab46ad3 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -932,6 +932,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";
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 435716e56a..b7c70dac3a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -96,6 +96,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:
break;
case TxoutType::PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();