Skip to content

Commit 3caee16

Browse files
committed
Merge #19953: Implement BIP 340-342 validation (Schnorr/taproot/tapscript)
0e2a5e4 tests: dumping and minimizing of script assets data (Pieter Wuille) 4567ba0 tests: add generic qa-asset-based script verification unit test (Pieter Wuille) f06e6d0 tests: functional tests for Schnorr/Taproot/Tapscript (Pieter Wuille) 3c22663 tests: add BIP340 Schnorr signature support to test framework (Pieter Wuille) 206fb18 --- [TAPROOT] Tests --- (Pieter Wuille) d7ff237 Activate Taproot/Tapscript on regtest (BIP 341, BIP 342) (Pieter Wuille) e9a021d Make Taproot spends standard + policy limits (Pieter Wuille) 865d2c3 --- [TAPROOT] Regtest activation and policy --- (Pieter Wuille) 72422ce Implement Tapscript script validation rules (BIP 342) (Johnson Lau) 330de89 Use ScriptExecutionData to pass through annex hash (Pieter Wuille) 8bbed4b Implement Taproot validation (BIP 341) (Pieter Wuille) 0664f5f Support for Schnorr signatures and integration in SignatureCheckers (BIP 340) (Pieter Wuille) 5de246c Implement Taproot signature hashing (BIP 341) (Johnson Lau) 9eb5908 Add TaggedHash function (BIP 340) (Pieter Wuille) 450d2b2 --- [TAPROOT] BIP340/341/342 consensus rules --- (Pieter Wuille) 5d62e3a refactor: keep spent outputs in PrecomputedTransactionData (Pieter Wuille) 8bd2b4e refactor: rename scriptPubKey in VerifyWitnessProgram to exec_script (Pieter Wuille) 107b57d scripted-diff: put ECDSA in name of signature functions (Pieter Wuille) f8c099e --- [TAPROOT] Refactors --- (Pieter Wuille) Pull request description: This is an implementation of the Schnorr/taproot consensus rules proposed by BIPs [340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), [341](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), and [342](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). See the list of commits [below](bitcoin/bitcoin#19953 (comment)). No signing or wallet support of any kind is included, as testing is done entirely through the Python test framework. This is a successor to bitcoin/bitcoin#17977 (see discussion following [this comment](bitcoin/bitcoin#17977 (comment))), and will have further changes squashed/rebased. The history of this PR can be found in #19997. ACKs for top commit: instagibbs: reACK bitcoin/bitcoin@0e2a5e4 benthecarman: reACK 0e2a5e4 kallewoof: reACK 0e2a5e4 jonasnick: ACK 0e2a5e4 almost only looked at bip340/libsecp related code jonatack: ACK 0e2a5e4 modulo the last four commits (tests) that I plan to finish reviewing tomorrow fjahr: reACK 0e2a5e4 achow101: ACK 0e2a5e4 Tree-SHA512: 1b00314450a2938a22bccbb4e177230cf08bd365d72055f9d526891f334b364c997e260c10bc19ca78440b6767712c9feea7faad9a1045dd51a5b96f7ca8146e
2 parents 8ed37f6 + 0e2a5e4 commit 3caee16

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2985
-121
lines changed

build_msvc/libsecp256k1/libsecp256k1.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</ItemGroup>
1313
<ItemDefinitionGroup>
1414
<ClCompile>
15-
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
15+
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
1616
<AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
1717
</ClCompile>
1818
</ItemDefinitionGroup>

ci/test/04_install.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,10 @@ else
8181
fi
8282

8383
if [ ! -d ${DIR_QA_ASSETS} ]; then
84-
if [ "$RUN_FUZZ_TESTS" = "true" ]; then
85-
DOCKER_EXEC git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
86-
fi
84+
DOCKER_EXEC git clone --depth=1 https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS}
8785
fi
8886
export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/
87+
export DIR_UNIT_TEST_DATA=${DIR_QA_ASSETS}/unit_test_data/
8988

9089
DOCKER_EXEC mkdir -p "${BASE_SCRATCH_DIR}/sanitizer-output/"
9190

ci/test/06_script_b.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ fi
2323

2424
if [ "$RUN_UNIT_TESTS" = "true" ]; then
2525
BEGIN_FOLD unit-tests
26-
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
26+
DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1
2727
END_FOLD
2828
fi
2929

3030
if [ "$RUN_UNIT_TESTS_SEQUENTIAL" = "true" ]; then
3131
BEGIN_FOLD unit-tests-seq
32-
DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
32+
DOCKER_EXEC DIR_UNIT_TEST_DATA=${DIR_UNIT_TEST_DATA} LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib "${BASE_BUILD_DIR}/bitcoin-*/src/test/test_bitcoin*" --catch_system_errors=no -l test_suite
3333
END_FOLD
3434
fi
3535

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,7 @@ if test x$need_bundled_univalue = xyes; then
17001700
AC_CONFIG_SUBDIRS([src/univalue])
17011701
fi
17021702

1703-
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery"
1703+
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental"
17041704
AC_CONFIG_SUBDIRS([src/secp256k1])
17051705

17061706
AC_OUTPUT

src/Makefile.test.include

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ FUZZ_TARGETS = \
129129
test/fuzz/script_deserialize \
130130
test/fuzz/script_flags \
131131
test/fuzz/script_interpreter \
132+
test/fuzz/script_assets_test_minimizer \
132133
test/fuzz/script_ops \
133134
test/fuzz/script_sigcache \
134135
test/fuzz/script_sign \
@@ -1083,6 +1084,12 @@ test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
10831084
test_fuzz_script_interpreter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
10841085
test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
10851086

1087+
test_fuzz_script_assets_test_minimizer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
1088+
test_fuzz_script_assets_test_minimizer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
1089+
test_fuzz_script_assets_test_minimizer_LDADD = $(FUZZ_SUITE_LD_COMMON)
1090+
test_fuzz_script_assets_test_minimizer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
1091+
test_fuzz_script_assets_test_minimizer_SOURCES = test/fuzz/script_assets_test_minimizer.cpp
1092+
10861093
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
10871094
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
10881095
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)

src/chainparams.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ class CMainParams : public CChainParams {
8686
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
8787
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
8888

89+
// Deployment of Taproot (BIPs 340-342)
90+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
91+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
92+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
93+
8994
// The best chain should have at least this much work.
9095
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000e1ab5ec9348e9f4b8eb8154");
9196

@@ -197,6 +202,11 @@ class CTestNetParams : public CChainParams {
197202
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
198203
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
199204

205+
// Deployment of Taproot (BIPs 340-342)
206+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
207+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
208+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
209+
200210
// The best chain should have at least this much work.
201211
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001495c1d5a01e2af8a23");
202212

@@ -380,6 +390,9 @@ class CRegTestParams : public CChainParams {
380390
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
381391
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
382392
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
393+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
394+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
395+
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
383396

384397
// The best chain should have at least this much work.
385398
consensus.nMinimumChainWork = uint256S("0x00");

src/consensus/params.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Consensus {
1414
enum DeploymentPos
1515
{
1616
DEPLOYMENT_TESTDUMMY,
17+
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
1718
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp
1819
MAX_VERSION_BITS_DEPLOYMENTS
1920
};

src/hash.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <crypto/common.h>
77
#include <crypto/hmac_sha512.h>
88

9+
#include <string>
910

1011
inline uint32_t ROTL32(uint32_t x, int8_t r)
1112
{
@@ -84,3 +85,12 @@ uint256 SHA256Uint256(const uint256& input)
8485
CSHA256().Write(input.begin(), 32).Finalize(result.begin());
8586
return result;
8687
}
88+
89+
CHashWriter TaggedHash(const std::string& tag)
90+
{
91+
CHashWriter writer(SER_GETHASH, 0);
92+
uint256 taghash;
93+
CSHA256().Write((const unsigned char*)tag.data(), tag.size()).Finalize(taghash.begin());
94+
writer << taghash << taghash;
95+
return writer;
96+
}

src/hash.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <uint256.h>
1616
#include <version.h>
1717

18+
#include <string>
1819
#include <vector>
1920

2021
typedef uint256 ChainCode;
@@ -202,4 +203,12 @@ unsigned int MurmurHash3(unsigned int nHashSeed, Span<const unsigned char> vData
202203

203204
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
204205

206+
/** Return a CHashWriter primed for tagged hashes (as specified in BIP 340).
207+
*
208+
* The returned object will have SHA256(tag) written to it twice (= 64 bytes).
209+
* A tagged hash can be computed by feeding the message into this object, and
210+
* then calling CHashWriter::GetSHA256().
211+
*/
212+
CHashWriter TaggedHash(const std::string& tag);
213+
205214
#endif // BITCOIN_HASH_H

src/policy/policy.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include <consensus/validation.h>
1111
#include <coins.h>
12-
12+
#include <span.h>
1313

1414
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
1515
{
@@ -206,6 +206,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
206206
// get the scriptPubKey corresponding to this input:
207207
CScript prevScript = prev.scriptPubKey;
208208

209+
bool p2sh = false;
209210
if (prevScript.IsPayToScriptHash()) {
210211
std::vector <std::vector<unsigned char> > stack;
211212
// 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)
216217
if (stack.empty())
217218
return false;
218219
prevScript = CScript(stack.back().begin(), stack.back().end());
220+
p2sh = true;
219221
}
220222

221223
int witnessversion = 0;
@@ -237,6 +239,36 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
237239
return false;
238240
}
239241
}
242+
243+
// Check policy limits for Taproot spends:
244+
// - MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE limit for stack item size
245+
// - No annexes
246+
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE && !p2sh) {
247+
// Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
248+
auto stack = MakeSpan(tx.vin[i].scriptWitness.stack);
249+
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
250+
// Annexes are nonstandard as long as no semantics are defined for them.
251+
return false;
252+
}
253+
if (stack.size() >= 2) {
254+
// Script path spend (2 or more stack elements after removing optional annex)
255+
const auto& control_block = SpanPopBack(stack);
256+
SpanPopBack(stack); // Ignore script
257+
if (control_block.empty()) return false; // Empty control block is invalid
258+
if ((control_block[0] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
259+
// Leaf version 0xc0 (aka Tapscript, see BIP 342)
260+
for (const auto& item : stack) {
261+
if (item.size() > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) return false;
262+
}
263+
}
264+
} else if (stack.size() == 1) {
265+
// Key path spend (1 stack element after removing optional annex)
266+
// (no policy rules apply)
267+
} else {
268+
// 0 stack elements; this is already invalid by consensus rules
269+
return false;
270+
}
271+
}
240272
}
241273
return true;
242274
}

0 commit comments

Comments
 (0)