Merge bitcoin/bitcoin#23394: Taproot wallet test vectors (generation+tests)

f1c33ee4ac tests: implement BIP341 test vectors (Pieter Wuille)
ac3037df11 tests: BIP341 test vector generation (Pieter Wuille)
ca83ffc2ea tests: add deterministic signing mode to ECDSA (Pieter Wuille)
c98c53f20c tests: abstract out precomputed BIP341 signature hash elements (Pieter Wuille)
a5bde018b4 tests: give feature_taproot access to sighash preimages (Pieter Wuille)
5140825096 tests: add more fields to TaprootInfo (Pieter Wuille)
2478c6730a Make signing follow BIP340 exactly w.r.t. aux randomness (Pieter Wuille)

Pull request description:

  This PR adds code to `test/functional/feature_taproot.py` which runs through a (deterministic) scenario covering several aspects of the wallet side of BIP341 (scriptPubKey computation from keys/scripts, control block computation, key path spending), with the ability to output test vectors in mediawiki format based on this scenario. The generated tests are then also included directly in `src/test/script_tests.cpp` and `src/test/script_standard_tests.cpp`.

  I intend to add these test vectors to BIP341 itself: https://github.com/bitcoin/bips/pull/1225

ACKs for top commit:
  laanwj:
    Code review ACK f1c33ee4ac

Tree-SHA512: fcf7109539cb214d3190516b205cd32d2b1b452f14aa66f4107acfaa8bfc7d368f626857f1935665a4342eabc0b9ee8aba608a7c0a2494bec0b498e723439c9d
This commit is contained in:
W. J. van der Laan committed 2021-11-15 20:32:42 +01:00
commit 5ccab7187b
13 files changed
+943 -55

No files matched your search

+1
View File
@@ -16,6 +16,7 @@ FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT)
JSON_TEST_FILES = \ JSON_TEST_FILES = \
test/data/script_tests.json \ test/data/script_tests.json \
test/data/bip341_wallet_vectors.json \
test/data/base58_encode_decode.json \ test/data/base58_encode_decode.json \
test/data/blockfilters.json \ test/data/blockfilters.json \
test/data/key_io_valid.json \ test/data/key_io_valid.json \
+2 -2
View File
@@ -275,7 +275,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
return true; return true;
} }
bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256* aux) const bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256& aux) const
{ {
assert(sig.size() == 64); assert(sig.size() == 64);
secp256k1_keypair keypair; secp256k1_keypair keypair;
@@ -288,7 +288,7 @@ bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint2
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root); uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false; if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
} }
bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, aux ? (unsigned char*)aux->data() : nullptr); bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, (unsigned char*)aux.data());
if (ret) { if (ret) {
// Additional verification step to prevent using a potentially corrupted signature // Additional verification step to prevent using a potentially corrupted signature
secp256k1_xonly_pubkey pubkey_verify; secp256k1_xonly_pubkey pubkey_verify;
+2 -2
View File
@@ -130,7 +130,7 @@ public:
/** /**
* Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this, * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this,
* optionally tweaked by *merkle_root. Additional nonce entropy can be provided through * optionally tweaked by *merkle_root. Additional nonce entropy is provided through
* aux. * aux.
* *
* merkle_root is used to optionally perform tweaking of the private key, as specified * merkle_root is used to optionally perform tweaking of the private key, as specified
@@ -143,7 +143,7 @@ public:
* (this is used for key path spending, with specific * (this is used for key path spending, with specific
* Merkle root of the script tree). * Merkle root of the script tree).
*/ */
bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root = nullptr, const uint256* aux = nullptr) const; bool SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint256* merkle_root, const uint256& aux) const;
//! Derive BIP32 child key. //! Derive BIP32 child key.
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
+1 -1
View File
@@ -1483,7 +1483,7 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo,
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash"); const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf"); const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch"); const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
+1
View File
@@ -229,6 +229,7 @@ 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_NODE_COUNT = 128;
static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
extern const CHashWriter HASHER_TAPSIGHASH; //!< Hasher with tag "TapSighash" pre-fed to it.
extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it. extern const CHashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it.
extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it. extern const CHashWriter HASHER_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it.
+2 -1
View File
@@ -81,7 +81,8 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
uint256 hash; uint256 hash;
if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false; if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
sig.resize(64); sig.resize(64);
if (!key.SignSchnorr(hash, sig, merkle_root, nullptr)) return false; // Use uint256{} as aux_rnd for now.
if (!key.SignSchnorr(hash, sig, merkle_root, {})) return false;
if (nHashType) sig.push_back(nHashType); if (nHashType) sig.push_back(nHashType);
return true; return true;
} }
+452
View File
@@ -0,0 +1,452 @@
{
"version": 1,
"scriptPubKey": [
{
"given": {
"internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",
"scriptTree": null
},
"intermediary": {
"merkleRoot": null,
"tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",
"tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343"
},
"expected": {
"scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",
"bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5"
}
},
{
"given": {
"internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",
"scriptTree": {
"id": 0,
"script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",
"leafVersion": 192
}
},
"intermediary": {
"leafHashes": [
"5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21"
],
"merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",
"tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",
"tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3"
},
"expected": {
"scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",
"bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586",
"scriptPathControlBlocks": [
"c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"
]
}
},
{
"given": {
"internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",
"scriptTree": {
"id": 0,
"script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac",
"leafVersion": 192
}
},
"intermediary": {
"leafHashes": [
"c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b"
],
"merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",
"tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",
"tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e"
},
"expected": {
"scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",
"bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5",
"scriptPathControlBlocks": [
"c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820"
]
}
},
{
"given": {
"internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",
"scriptTree": [
{
"id": 0,
"script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac",
"leafVersion": 192
},
{
"id": 1,
"script": "06424950333431",
"leafVersion": 250
}
]
},
"intermediary": {
"leafHashes": [
"8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7",
"f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a"
],
"merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",
"tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",
"tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5"
},
"expected": {
"scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",
"bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm",
"scriptPathControlBlocks": [
"c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a",
"faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
]
}
},
{
"given": {
"internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",
"scriptTree": [
{
"id": 0,
"script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac",
"leafVersion": 192
},
{
"id": 1,
"script": "07546170726f6f74",
"leafVersion": 192
}
]
},
"intermediary": {
"leafHashes": [
"64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89",
"2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb"
],
"merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",
"tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",
"tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220"
},
"expected": {
"scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",
"bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq",
"scriptPathControlBlocks": [
"c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb",
"c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89"
]
}
},
{
"given": {
"internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
"scriptTree": [
{
"id": 0,
"script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac",
"leafVersion": 192
},
[
{
"id": 1,
"script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac",
"leafVersion": 192
},
{
"id": 2,
"script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac",
"leafVersion": 192
}
]
]
},
"intermediary": {
"leafHashes": [
"2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",
"ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c",
"9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6"
],
"merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",
"tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",
"tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605"
},
"expected": {
"scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
"bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",
"scriptPathControlBlocks": [
"c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553",
"c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",
"c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"
]
}
},
{
"given": {
"internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
"scriptTree": [
{
"id": 0,
"script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac",
"leafVersion": 192
},
[
{
"id": 1,
"script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac",
"leafVersion": 192
},
{
"id": 2,
"script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac",
"leafVersion": 192
}
]
]
},
"intermediary": {
"leafHashes": [
"f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",
"737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711",
"d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7"
],
"merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",
"tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",
"tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831"
},
"expected": {
"scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
"bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",
"scriptPathControlBlocks": [
"c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91",
"c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",
"c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"
]
}
}
],
"keyPathSpending": [
{
"given": {
"rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d",
"utxosSpent": [
{
"scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",
"amountSats": 420000000
},
{
"scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",
"amountSats": 462000000
},
{
"scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac",
"amountSats": 294000000
},
{
"scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",
"amountSats": 504000000
},
{
"scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",
"amountSats": 630000000
},
{
"scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc",
"amountSats": 378000000
},
{
"scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",
"amountSats": 672000000
},
{
"scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",
"amountSats": 546000000
},
{
"scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",
"amountSats": 588000000
}
]
},
"intermediary": {
"hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6",
"hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5",
"hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f",
"hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21",
"hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e"
},
"inputSpending": [
{
"given": {
"txinIndex": 0,
"internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa",
"merkleRoot": null,
"hashType": 3
},
"intermediary": {
"internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",
"tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",
"tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9",
"sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0",
"precomputedUsed": [
"hashAmounts",
"hashPrevouts",
"hashScriptPubkeys",
"hashSequences"
],
"sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555"
},
"expected": {
"witness": [
"ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03"
]
}
},
{
"given": {
"txinIndex": 1,
"internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f",
"merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",
"hashType": 131
},
"intermediary": {
"internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",
"tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",
"tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080",
"sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d",
"precomputedUsed": [],
"sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d"
},
"expected": {
"witness": [
"052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83"
]
}
},
{
"given": {
"txinIndex": 3,
"internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64",
"merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",
"hashType": 1
},
"intermediary": {
"internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",
"tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",
"tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d",
"sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000",
"precomputedUsed": [
"hashAmounts",
"hashOutputs",
"hashPrevouts",
"hashScriptPubkeys",
"hashSequences"
],
"sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669"
},
"expected": {
"witness": [
"ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01"
]
}
},
{
"given": {
"txinIndex": 4,
"internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e",
"merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",
"hashType": 0
},
"intermediary": {
"internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",
"tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",
"tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501",
"sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000",
"precomputedUsed": [
"hashAmounts",
"hashOutputs",
"hashPrevouts",
"hashScriptPubkeys",
"hashSequences"
],
"sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef"
},
"expected": {
"witness": [
"b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f"
]
}
},
{
"given": {
"txinIndex": 6,
"internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8",
"merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",
"hashType": 2
},
"intermediary": {
"internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",
"tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",
"tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901",
"sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000",
"precomputedUsed": [
"hashAmounts",
"hashPrevouts",
"hashScriptPubkeys",
"hashSequences"
],
"sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85"
},
"expected": {
"witness": [
"a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002"
]
}
},
{
"given": {
"txinIndex": 7,
"internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103",
"merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",
"hashType": 130
},
"intermediary": {
"internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",
"tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",
"tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b",
"sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff",
"precomputedUsed": [],
"sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10"
},
"expected": {
"witness": [
"ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482"
]
}
},
{
"given": {
"txinIndex": 8,
"internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa",
"merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",
"hashType": 129
},
"intermediary": {
"internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",
"tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",
"tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5",
"sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff",
"precomputedUsed": [
"hashOutputs"
],
"sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2"
},
"expected": {
"witness": [
"bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981"
]
}
}
],
"auxiliary": {
"fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d"
}
}
]
}
+2 -2
View File
@@ -321,7 +321,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
key.Set(sec.begin(), sec.end(), true); key.Set(sec.begin(), sec.end(), true);
XOnlyPubKey pubkey(key.GetPubKey()); XOnlyPubKey pubkey(key.GetPubKey());
BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end())); BOOST_CHECK(std::equal(pubkey.begin(), pubkey.end(), pub.begin(), pub.end()));
bool ok = key.SignSchnorr(msg256, sig64, nullptr, &aux256); bool ok = key.SignSchnorr(msg256, sig64, nullptr, aux256);
BOOST_CHECK(ok); BOOST_CHECK(ok);
BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig); BOOST_CHECK(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
// Verify those signatures for good measure. // Verify those signatures for good measure.
@@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
BOOST_CHECK(tweaked); BOOST_CHECK(tweaked);
XOnlyPubKey tweaked_key = tweaked->first; XOnlyPubKey tweaked_key = tweaked->first;
aux256 = InsecureRand256(); aux256 = InsecureRand256();
bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, &aux256); bool ok = key.SignSchnorr(msg256, sig64, &merkle_root, aux256);
BOOST_CHECK(ok); BOOST_CHECK(ok);
BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64)); BOOST_CHECK(tweaked_key.VerifySchnorr(msg256, sig64));
} }
+46
View File
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/data/bip341_wallet_vectors.json.h>
#include <key.h> #include <key.h>
#include <key_io.h> #include <key_io.h>
#include <script/script.h> #include <script/script.h>
@@ -12,6 +14,8 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <univalue.h>
BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup)
@@ -385,4 +389,46 @@ BOOST_AUTO_TEST_CASE(script_standard_taproot_builder)
BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge"); BOOST_CHECK_EQUAL(EncodeDestination(builder.GetOutput()), "bc1pj6gaw944fy0xpmzzu45ugqde4rz7mqj5kj0tg8kmr5f0pjq8vnaqgynnge");
} }
BOOST_AUTO_TEST_CASE(bip341_spk_test_vectors)
{
using control_set = decltype(TaprootSpendData::scripts)::mapped_type;
UniValue tests;
tests.read((const char*)json_tests::bip341_wallet_vectors, sizeof(json_tests::bip341_wallet_vectors));
const auto& vectors = tests["scriptPubKey"];
for (const auto& vec : vectors.getValues()) {
TaprootBuilder spktest;
std::map<std::pair<CScript, int>, int> scriptposes;
std::function<void (const UniValue&, int)> parse_tree = [&](const UniValue& node, int depth) {
if (node.isNull()) return;
if (node.isObject()) {
auto script_bytes = ParseHex(node["script"].get_str());
CScript script(script_bytes.begin(), script_bytes.end());
int idx = node["id"].get_int();
int leaf_version = node["leafVersion"].get_int();
scriptposes[{script, leaf_version}] = idx;
spktest.Add(depth, script, leaf_version);
} else {
parse_tree(node[0], depth + 1);
parse_tree(node[1], depth + 1);
}
};
parse_tree(vec["given"]["scriptTree"], 0);
spktest.Finalize(XOnlyPubKey(ParseHex(vec["given"]["internalPubkey"].get_str())));
BOOST_CHECK_EQUAL(HexStr(GetScriptForDestination(spktest.GetOutput())), vec["expected"]["scriptPubKey"].get_str());
BOOST_CHECK_EQUAL(EncodeDestination(spktest.GetOutput()), vec["expected"]["bip350Address"].get_str());
auto spend_data = spktest.GetSpendData();
BOOST_CHECK_EQUAL(vec["intermediary"]["merkleRoot"].isNull(), spend_data.merkle_root.IsNull());
if (!spend_data.merkle_root.IsNull()) {
BOOST_CHECK_EQUAL(vec["intermediary"]["merkleRoot"].get_str(), HexStr(spend_data.merkle_root));
}
BOOST_CHECK_EQUAL(spend_data.scripts.size(), scriptposes.size());
for (const auto& scriptpos : scriptposes) {
BOOST_CHECK(spend_data.scripts[scriptpos.first] == control_set{ParseHex(vec["expected"]["scriptPathControlBlocks"][scriptpos.second].get_str())});
}
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
+76
View File
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/data/script_tests.json.h> #include <test/data/script_tests.json.h>
#include <test/data/bip341_wallet_vectors.json.h>
#include <core_io.h> #include <core_io.h>
#include <fs.h> #include <fs.h>
@@ -1743,4 +1744,79 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
file.close(); file.close();
} }
BOOST_AUTO_TEST_CASE(bip341_keypath_test_vectors)
{
UniValue tests;
tests.read((const char*)json_tests::bip341_wallet_vectors, sizeof(json_tests::bip341_wallet_vectors));
const auto& vectors = tests["keyPathSpending"];
for (const auto& vec : vectors.getValues()) {
auto txhex = ParseHex(vec["given"]["rawUnsignedTx"].get_str());
CMutableTransaction tx;
VectorReader(SER_NETWORK, PROTOCOL_VERSION, txhex, 0) >> tx;
std::vector<CTxOut> utxos;
for (const auto& utxo_spent : vec["given"]["utxosSpent"].getValues()) {
auto script_bytes = ParseHex(utxo_spent["scriptPubKey"].get_str());
CScript script{script_bytes.begin(), script_bytes.end()};
CAmount amount{utxo_spent["amountSats"].get_int()};
utxos.emplace_back(amount, script);
}
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>{utxos}, true);
BOOST_CHECK(txdata.m_bip341_taproot_ready);
BOOST_CHECK_EQUAL(HexStr(txdata.m_spent_amounts_single_hash), vec["intermediary"]["hashAmounts"].get_str());
BOOST_CHECK_EQUAL(HexStr(txdata.m_outputs_single_hash), vec["intermediary"]["hashOutputs"].get_str());
BOOST_CHECK_EQUAL(HexStr(txdata.m_prevouts_single_hash), vec["intermediary"]["hashPrevouts"].get_str());
BOOST_CHECK_EQUAL(HexStr(txdata.m_spent_scripts_single_hash), vec["intermediary"]["hashScriptPubkeys"].get_str());
BOOST_CHECK_EQUAL(HexStr(txdata.m_sequences_single_hash), vec["intermediary"]["hashSequences"].get_str());
for (const auto& input : vec["inputSpending"].getValues()) {
int txinpos = input["given"]["txinIndex"].get_int();
int hashtype = input["given"]["hashType"].get_int();
// Load key.
auto privkey = ParseHex(input["given"]["internalPrivkey"].get_str());
CKey key;
key.Set(privkey.begin(), privkey.end(), true);
// Load Merkle root.
uint256 merkle_root;
if (!input["given"]["merkleRoot"].isNull()) {
merkle_root = uint256{ParseHex(input["given"]["merkleRoot"].get_str())};
}
// Compute and verify (internal) public key.
XOnlyPubKey pubkey{key.GetPubKey()};
BOOST_CHECK_EQUAL(HexStr(pubkey), input["intermediary"]["internalPubkey"].get_str());
// Sign and verify signature.
FlatSigningProvider provider;
provider.keys[key.GetPubKey().GetID()] = key;
MutableTransactionSignatureCreator creator(&tx, txinpos, utxos[txinpos].nValue, &txdata, hashtype);
std::vector<unsigned char> signature;
BOOST_CHECK(creator.CreateSchnorrSig(provider, signature, pubkey, nullptr, &merkle_root, SigVersion::TAPROOT));
BOOST_CHECK_EQUAL(HexStr(signature), input["expected"]["witness"][0].get_str());
// We can't observe the tweak used inside the signing logic, so verify by recomputing it.
BOOST_CHECK_EQUAL(HexStr(pubkey.ComputeTapTweakHash(merkle_root.IsNull() ? nullptr : &merkle_root)), input["intermediary"]["tweak"].get_str());
// We can't observe the sighash used inside the signing logic, so verify by recomputing it.
ScriptExecutionData sed;
sed.m_annex_init = true;
sed.m_annex_present = false;
uint256 sighash;
BOOST_CHECK(SignatureHashSchnorr(sighash, sed, tx, txinpos, hashtype, SigVersion::TAPROOT, txdata, MissingDataBehavior::FAIL));
BOOST_CHECK_EQUAL(HexStr(sighash), input["intermediary"]["sigHash"].get_str());
// To verify the sigmsg, hash the expected sigmsg, and compare it with the (expected) sighash.
BOOST_CHECK_EQUAL(HexStr((CHashWriter(HASHER_TAPSIGHASH) << MakeSpan(ParseHex(input["intermediary"]["sigMsg"].get_str()))).GetSHA256()), input["intermediary"]["sigHash"].get_str());
}
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
+283 -18
View File
@@ -22,11 +22,17 @@ from test_framework.messages import (
) )
from test_framework.script import ( from test_framework.script import (
ANNEX_TAG, ANNEX_TAG,
BIP341_sha_amounts,
BIP341_sha_outputs,
BIP341_sha_prevouts,
BIP341_sha_scriptpubkeys,
BIP341_sha_sequences,
CScript, CScript,
CScriptNum, CScriptNum,
CScriptOp, CScriptOp,
hash256,
LEAF_VERSION_TAPSCRIPT, LEAF_VERSION_TAPSCRIPT,
LegacySignatureHash, LegacySignatureMsg,
LOCKTIME_THRESHOLD, LOCKTIME_THRESHOLD,
MAX_SCRIPT_ELEMENT_SIZE, MAX_SCRIPT_ELEMENT_SIZE,
OP_0, OP_0,
@@ -70,13 +76,15 @@ from test_framework.script import (
SIGHASH_NONE, SIGHASH_NONE,
SIGHASH_SINGLE, SIGHASH_SINGLE,
SIGHASH_ANYONECANPAY, SIGHASH_ANYONECANPAY,
SegwitV0SignatureHash, SegwitV0SignatureMsg,
TaprootSignatureHash, TaggedHash,
TaprootSignatureMsg,
is_op_success, is_op_success,
taproot_construct, taproot_construct,
) )
from test_framework.script_util import ( from test_framework.script_util import (
key_to_p2pk_script, key_to_p2pk_script,
key_to_p2pkh_script,
key_to_p2wpkh_script, key_to_p2wpkh_script,
keyhash_to_p2pkh_script, keyhash_to_p2pkh_script,
script_to_p2sh_script, script_to_p2sh_script,
@@ -87,6 +95,7 @@ from test_framework.util import assert_raises_rpc_error, assert_equal
from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey
from test_framework.address import ( from test_framework.address import (
hash160, hash160,
program_to_witness
) )
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
from io import BytesIO from io import BytesIO
@@ -95,6 +104,9 @@ import hashlib
import os import os
import random import random
# Whether or not to output generated test vectors, in JSON format.
GEN_TEST_VECTORS = False
# === Framework for building spending transactions. === # === Framework for building spending transactions. ===
# #
# The computation is represented as a "context" dict, whose entries store potentially-unevaluated expressions that # The computation is represented as a "context" dict, whose entries store potentially-unevaluated expressions that
@@ -194,8 +206,8 @@ def default_controlblock(ctx):
"""Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch.""" """Default expression for "controlblock": combine leafversion, negflag, pubkey_internal, merklebranch."""
return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch") return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch")
def default_sighash(ctx): def default_sigmsg(ctx):
"""Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash.""" """Default expression for "sigmsg": depending on mode, compute BIP341, BIP143, or legacy sigmsg."""
tx = get(ctx, "tx") tx = get(ctx, "tx")
idx = get(ctx, "idx") idx = get(ctx, "idx")
hashtype = get(ctx, "hashtype_actual") hashtype = get(ctx, "hashtype_actual")
@@ -208,18 +220,30 @@ def default_sighash(ctx):
codeseppos = get(ctx, "codeseppos") codeseppos = get(ctx, "codeseppos")
leaf_ver = get(ctx, "leafversion") leaf_ver = get(ctx, "leafversion")
script = get(ctx, "script_taproot") script = get(ctx, "script_taproot")
return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex) return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex)
else: else:
return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=False, annex=annex) return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
elif mode == "witv0": elif mode == "witv0":
# BIP143 signature hash # BIP143 signature hash
scriptcode = get(ctx, "scriptcode") scriptcode = get(ctx, "scriptcode")
utxos = get(ctx, "utxos") utxos = get(ctx, "utxos")
return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue) return SegwitV0SignatureMsg(scriptcode, tx, idx, hashtype, utxos[idx].nValue)
else: else:
# Pre-segwit signature hash # Pre-segwit signature hash
scriptcode = get(ctx, "scriptcode") scriptcode = get(ctx, "scriptcode")
return LegacySignatureHash(scriptcode, tx, idx, hashtype)[0] return LegacySignatureMsg(scriptcode, tx, idx, hashtype)[0]
def default_sighash(ctx):
"""Default expression for "sighash": depending on mode, compute tagged hash or dsha256 of sigmsg."""
msg = get(ctx, "sigmsg")
mode = get(ctx, "mode")
if mode == "taproot":
return TaggedHash("TapSighash", msg)
else:
if msg is None:
return (1).to_bytes(32, 'little')
else:
return hash256(msg)
def default_tweak(ctx): def default_tweak(ctx):
"""Default expression for "tweak": None if a leaf is specified, tap[0] otherwise.""" """Default expression for "tweak": None if a leaf is specified, tap[0] otherwise."""
@@ -239,14 +263,18 @@ def default_key_tweaked(ctx):
def default_signature(ctx): def default_signature(ctx):
"""Default expression for "signature": BIP340 signature or ECDSA signature depending on mode.""" """Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
sighash = get(ctx, "sighash") sighash = get(ctx, "sighash")
deterministic = get(ctx, "deterministic")
if get(ctx, "mode") == "taproot": if get(ctx, "mode") == "taproot":
key = get(ctx, "key_tweaked") key = get(ctx, "key_tweaked")
flip_r = get(ctx, "flag_flip_r") flip_r = get(ctx, "flag_flip_r")
flip_p = get(ctx, "flag_flip_p") flip_p = get(ctx, "flag_flip_p")
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p) aux = bytes([0] * 32)
if not deterministic:
aux = random.getrandbits(256).to_bytes(32, 'big')
return sign_schnorr(key, sighash, flip_r=flip_r, flip_p=flip_p, aux=aux)
else: else:
key = get(ctx, "key") key = get(ctx, "key")
return key.sign_ecdsa(sighash) return key.sign_ecdsa(sighash, rfc6979=deterministic)
def default_hashtype_actual(ctx): def default_hashtype_actual(ctx):
"""Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot.""" """Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
@@ -340,6 +368,8 @@ DEFAULT_CONTEXT = {
"key_tweaked": default_key_tweaked, "key_tweaked": default_key_tweaked,
# The tweak to use (None for script path spends, the actual tweak for key path spends). # The tweak to use (None for script path spends, the actual tweak for key path spends).
"tweak": default_tweak, "tweak": default_tweak,
# The sigmsg value (preimage of sighash)
"sigmsg": default_sigmsg,
# The sighash value (32 bytes) # The sighash value (32 bytes)
"sighash": default_sighash, "sighash": default_sighash,
# The information about the chosen script path spend (TaprootLeafInfo object). # The information about the chosen script path spend (TaprootLeafInfo object).
@@ -376,6 +406,8 @@ DEFAULT_CONTEXT = {
"leaf": None, "leaf": None,
# The input arguments to provide to the executed script # The input arguments to provide to the executed script
"inputs": [], "inputs": [],
# Use deterministic signing nonces
"deterministic": False,
# == Parameters to be set before evaluation: == # == Parameters to be set before evaluation: ==
# - mode: what spending style to use ("taproot", "witv0", or "legacy"). # - mode: what spending style to use ("taproot", "witv0", or "legacy").
@@ -396,6 +428,7 @@ def flatten(lst):
ret.append(elem) ret.append(elem)
return ret return ret
def spend(tx, idx, utxos, **kwargs): def spend(tx, idx, utxos, **kwargs):
"""Sign transaction input idx of tx, provided utxos is the list of outputs being spent. """Sign transaction input idx of tx, provided utxos is the list of outputs being spent.
@@ -1253,6 +1286,14 @@ class TaprootTest(BitcoinTestFramework):
else: else:
assert node.getbestblockhash() == self.lastblockhash, "Failed to reject: " + msg assert node.getbestblockhash() == self.lastblockhash, "Failed to reject: " + msg
def init_blockinfo(self, node):
# Initialize variables used by block_submit().
self.lastblockhash = node.getbestblockhash()
self.tip = int(self.lastblockhash, 16)
block = node.getblock(self.lastblockhash)
self.lastblockheight = block['height']
self.lastblocktime = block['time']
def test_spenders(self, node, spenders, input_counts): def test_spenders(self, node, spenders, input_counts):
"""Run randomized tests with a number of "spenders". """Run randomized tests with a number of "spenders".
@@ -1279,12 +1320,7 @@ class TaprootTest(BitcoinTestFramework):
host_spks.append(spk) host_spks.append(spk)
host_pubkeys.append(bytes.fromhex(info['pubkey'])) host_pubkeys.append(bytes.fromhex(info['pubkey']))
# Initialize variables used by block_submit(). self.init_blockinfo(node)
self.lastblockhash = node.getbestblockhash()
self.tip = int(self.lastblockhash, 16)
block = node.getblock(self.lastblockhash)
self.lastblockheight = block['height']
self.lastblocktime = block['time']
# Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and # Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and
# one change output at the end. The transaction is constructed on the Python side to enable # one change output at the end. The transaction is constructed on the Python side to enable
@@ -1458,10 +1494,239 @@ class TaprootTest(BitcoinTestFramework):
assert len(mismatching_utxos) == 0 assert len(mismatching_utxos) == 0
self.log.info(" - Done") self.log.info(" - Done")
def gen_test_vectors(self):
"""Run a scenario that corresponds (and optionally produces) to BIP341 test vectors."""
self.log.info("Unit test scenario...")
# Deterministically mine coins to OP_TRUE in block 1
assert self.nodes[1].getblockcount() == 0
coinbase = CTransaction()
coinbase.nVersion = 1
coinbase.vin = [CTxIn(COutPoint(0, 0xffffffff), CScript([OP_1, OP_1]), 0xffffffff)]
coinbase.vout = [CTxOut(5000000000, CScript([OP_1]))]
coinbase.nLockTime = 0
coinbase.rehash()
assert coinbase.hash == "f60c73405d499a956d3162e3483c395526ef78286458a4cb17b125aa92e49b20"
# Mine it
block = create_block(hashprev=int(self.nodes[1].getbestblockhash(), 16), coinbase=coinbase)
block.rehash()
block.solve()
self.nodes[1].submitblock(block.serialize().hex())
assert self.nodes[1].getblockcount() == 1
self.generate(self.nodes[1], COINBASE_MATURITY)
SEED = 317
VALID_LEAF_VERS = list(range(0xc0, 0x100, 2)) + [0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe]
# Generate private keys
prvs = [hashlib.sha256(SEED.to_bytes(2, 'big') + bytes([i])).digest() for i in range(100)]
# Generate corresponding public x-only pubkeys
pubs = [compute_xonly_pubkey(prv)[0] for prv in prvs]
# Generate taproot objects
inner_keys = [pubs[i] for i in range(7)]
script_lists = [
None,
[("0", CScript([pubs[50], OP_CHECKSIG]), 0xc0)],
[("0", CScript([pubs[51], OP_CHECKSIG]), 0xc0)],
[("0", CScript([pubs[52], OP_CHECKSIG]), 0xc0), ("1", CScript([b"BIP341"]), VALID_LEAF_VERS[pubs[99][0] % 41])],
[("0", CScript([pubs[53], OP_CHECKSIG]), 0xc0), ("1", CScript([b"Taproot"]), VALID_LEAF_VERS[pubs[99][1] % 41])],
[("0", CScript([pubs[54], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[55], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[56], OP_CHECKSIG]), 0xc0)]],
[("0", CScript([pubs[57], OP_CHECKSIG]), 0xc0), [("1", CScript([pubs[58], OP_CHECKSIG]), 0xc0), ("2", CScript([pubs[59], OP_CHECKSIG]), 0xc0)]],
]
taps = [taproot_construct(inner_keys[i], script_lists[i]) for i in range(len(inner_keys))]
# Require negated taps[0]
assert taps[0].negflag
# Require one negated and one non-negated in taps 1 and 2.
assert taps[1].negflag != taps[2].negflag
# Require one negated and one non-negated in taps 3 and 4.
assert taps[3].negflag != taps[4].negflag
# Require one negated and one non-negated in taps 5 and 6.
assert taps[5].negflag != taps[6].negflag
cblks = [{leaf: get({**DEFAULT_CONTEXT, 'tap': taps[i], 'leaf': leaf}, 'controlblock') for leaf in taps[i].leaves} for i in range(7)]
# Require one swapped and one unswapped in taps 3 and 4.
assert (cblks[3]['0'][33:65] < cblks[3]['1'][33:65]) != (cblks[4]['0'][33:65] < cblks[4]['1'][33:65])
# Require one swapped and one unswapped in taps 5 and 6, both at the top and child level.
assert (cblks[5]['0'][33:65] < cblks[5]['1'][65:]) != (cblks[6]['0'][33:65] < cblks[6]['1'][65:])
assert (cblks[5]['1'][33:65] < cblks[5]['2'][33:65]) != (cblks[6]['1'][33:65] < cblks[6]['2'][33:65])
# Require within taps 5 (and thus also 6) that one level is swapped and the other is not.
assert (cblks[5]['0'][33:65] < cblks[5]['1'][65:]) != (cblks[5]['1'][33:65] < cblks[5]['2'][33:65])
# Compute a deterministic set of scriptPubKeys
tap_spks = []
old_spks = []
spend_info = {}
# First, taproot scriptPubKeys, for the tap objects constructed above
for i, tap in enumerate(taps):
tap_spks.append(tap.scriptPubKey)
d = {'key': prvs[i], 'tap': tap, 'mode': 'taproot'}
spend_info[tap.scriptPubKey] = d
# Then, a number of deterministically generated (keys 0x1,0x2,0x3) with 2x P2PKH, 1x P2WPKH spks.
for i in range(1, 4):
prv = ECKey()
prv.set(i.to_bytes(32, 'big'), True)
pub = prv.get_pubkey().get_bytes()
d = {"key": prv}
d["scriptcode"] = key_to_p2pkh_script(pub)
d["inputs"] = [getter("sign"), pub]
if i < 3:
# P2PKH
d['spk'] = key_to_p2pkh_script(pub)
d['mode'] = 'legacy'
else:
# P2WPKH
d['spk'] = key_to_p2wpkh_script(pub)
d['mode'] = 'witv0'
old_spks.append(d['spk'])
spend_info[d['spk']] = d
# Construct a deterministic chain of transactions creating UTXOs to the test's spk's (so that they
# come from distinct txids).
txn = []
lasttxid = coinbase.sha256
amount = 5000000000
for i, spk in enumerate(old_spks + tap_spks):
val = 42000000 * (i + 7)
tx = CTransaction()
tx.nVersion = 1
tx.vin = [CTxIn(COutPoint(lasttxid, i & 1), CScript([]), 0xffffffff)]
tx.vout = [CTxOut(val, spk), CTxOut(amount - val, CScript([OP_1]))]
if i & 1:
tx.vout = list(reversed(tx.vout))
tx.nLockTime = 0
tx.rehash()
amount -= val
lasttxid = tx.sha256
txn.append(tx)
spend_info[spk]['prevout'] = COutPoint(tx.sha256, i & 1)
spend_info[spk]['utxo'] = CTxOut(val, spk)
# Mine those transactions
self.init_blockinfo(self.nodes[1])
self.block_submit(self.nodes[1], txn, "Crediting txn", None, sigops_weight=10, accept=True)
# scriptPubKey computation
tests = {"version": 1}
spk_tests = tests.setdefault("scriptPubKey", [])
for i, tap in enumerate(taps):
test_case = {}
given = test_case.setdefault("given", {})
given['internalPubkey'] = tap.internal_pubkey.hex()
def pr(node):
if node is None:
return None
elif isinstance(node, tuple):
return {"id": int(node[0]), "script": node[1].hex(), "leafVersion": node[2]}
elif len(node) == 1:
return pr(node[0])
elif len(node) == 2:
return [pr(node[0]), pr(node[1])]
else:
assert False
given['scriptTree'] = pr(script_lists[i])
intermediary = test_case.setdefault("intermediary", {})
if len(tap.leaves):
leafhashes = intermediary.setdefault('leafHashes', [None] * len(tap.leaves))
for leaf in tap.leaves:
leafhashes[int(leaf)] = tap.leaves[leaf].leaf_hash.hex()
intermediary['merkleRoot'] = tap.merkle_root.hex() if tap.merkle_root else None
intermediary['tweak'] = tap.tweak.hex()
intermediary['tweakedPubkey'] = tap.output_pubkey.hex()
expected = test_case.setdefault("expected", {})
expected['scriptPubKey'] = tap.scriptPubKey.hex()
expected['bip350Address'] = program_to_witness(1, bytes(tap.output_pubkey), True)
if len(tap.leaves):
control_blocks = expected.setdefault("scriptPathControlBlocks", [None] * len(tap.leaves))
for leaf in tap.leaves:
ctx = {**DEFAULT_CONTEXT, 'tap': tap, 'leaf': leaf}
control_blocks[int(leaf)] = get(ctx, "controlblock").hex()
spk_tests.append(test_case)
# Construct a deterministic transaction spending all outputs created above.
tx = CTransaction()
tx.nVersion = 2
tx.vin = []
inputs = []
input_spks = [tap_spks[0], tap_spks[1], old_spks[0], tap_spks[2], tap_spks[5], old_spks[2], tap_spks[6], tap_spks[3], tap_spks[4]]
sequences = [0, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffffe, 0, 0, 0xffffffff, 0xffffffff]
hashtypes = [SIGHASH_SINGLE, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, SIGHASH_ALL, SIGHASH_ALL, SIGHASH_DEFAULT, SIGHASH_ALL, SIGHASH_NONE, SIGHASH_NONE|SIGHASH_ANYONECANPAY, SIGHASH_ALL|SIGHASH_ANYONECANPAY]
for i, spk in enumerate(input_spks):
tx.vin.append(CTxIn(spend_info[spk]['prevout'], CScript(), sequences[i]))
inputs.append(spend_info[spk]['utxo'])
tx.vout.append(CTxOut(1000000000, old_spks[1]))
tx.vout.append(CTxOut(3410000000, pubs[98]))
tx.nLockTime = 500000000
precomputed = {
"hashAmounts": BIP341_sha_amounts(inputs),
"hashPrevouts": BIP341_sha_prevouts(tx),
"hashScriptPubkeys": BIP341_sha_scriptpubkeys(inputs),
"hashSequences": BIP341_sha_sequences(tx),
"hashOutputs": BIP341_sha_outputs(tx)
}
keypath_tests = tests.setdefault("keyPathSpending", [])
tx_test = {}
global_given = tx_test.setdefault("given", {})
global_given['rawUnsignedTx'] = tx.serialize().hex()
utxos_spent = global_given.setdefault("utxosSpent", [])
for i in range(len(input_spks)):
utxos_spent.append({"scriptPubKey": inputs[i].scriptPubKey.hex(), "amountSats": inputs[i].nValue})
global_intermediary = tx_test.setdefault("intermediary", {})
for key in sorted(precomputed.keys()):
global_intermediary[key] = precomputed[key].hex()
test_list = tx_test.setdefault('inputSpending', [])
for i in range(len(input_spks)):
ctx = {
**DEFAULT_CONTEXT,
**spend_info[input_spks[i]],
'tx': tx,
'utxos': inputs,
'idx': i,
'hashtype': hashtypes[i],
'deterministic': True
}
if ctx['mode'] == 'taproot':
test_case = {}
given = test_case.setdefault("given", {})
given['txinIndex'] = i
given['internalPrivkey'] = get(ctx, 'key').hex()
if get(ctx, "tap").merkle_root != bytes():
given['merkleRoot'] = get(ctx, "tap").merkle_root.hex()
else:
given['merkleRoot'] = None
given['hashType'] = get(ctx, "hashtype")
intermediary = test_case.setdefault("intermediary", {})
intermediary['internalPubkey'] = get(ctx, "tap").internal_pubkey.hex()
intermediary['tweak'] = get(ctx, "tap").tweak.hex()
intermediary['tweakedPrivkey'] = get(ctx, "key_tweaked").hex()
sigmsg = get(ctx, "sigmsg")
intermediary['sigMsg'] = sigmsg.hex()
intermediary['precomputedUsed'] = [key for key in sorted(precomputed.keys()) if sigmsg.count(precomputed[key])]
intermediary['sigHash'] = get(ctx, "sighash").hex()
expected = test_case.setdefault("expected", {})
expected['witness'] = [get(ctx, "sign").hex()]
test_list.append(test_case)
tx.wit.vtxinwit.append(CTxInWitness())
tx.vin[i].scriptSig = CScript(flatten(get(ctx, "scriptsig")))
tx.wit.vtxinwit[i].scriptWitness.stack = flatten(get(ctx, "witness"))
aux = tx_test.setdefault("auxiliary", {})
aux['fullySignedTx'] = tx.serialize().hex()
keypath_tests.append(tx_test)
assert_equal(hashlib.sha256(tx.serialize()).hexdigest(), "24bab662cb55a7f3bae29b559f651674c62bcc1cd442d44715c0133939107b38")
# Mine the spending transaction
self.block_submit(self.nodes[1], [tx], "Spending txn", None, sigops_weight=10000, accept=True, witness=True)
if GEN_TEST_VECTORS:
print(json.dumps(tests, indent=4, sort_keys=False))
def run_test(self): def run_test(self):
self.gen_test_vectors()
# Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot). # Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
self.log.info("Post-activation tests...") self.log.info("Post-activation tests...")
self.generate(self.nodes[1], COINBASE_MATURITY + 1)
self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3]) self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
# Re-connect nodes in case they have been disconnected # Re-connect nodes in case they have been disconnected
+17 -3
View File
@@ -8,6 +8,7 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for
anything but tests.""" anything but tests."""
import csv import csv
import hashlib import hashlib
import hmac
import os import os
import random import random
import unittest import unittest
@@ -326,6 +327,16 @@ def generate_privkey():
"""Generate a valid random 32-byte private key.""" """Generate a valid random 32-byte private key."""
return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big') return random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big')
def rfc6979_nonce(key):
"""Compute signing nonce using RFC6979."""
v = bytes([1] * 32)
k = bytes([0] * 32)
k = hmac.new(k, v + b"\x00" + key, 'sha256').digest()
v = hmac.new(k, v, 'sha256').digest()
k = hmac.new(k, v + b"\x01" + key, 'sha256').digest()
v = hmac.new(k, v, 'sha256').digest()
return hmac.new(k, v, 'sha256').digest()
class ECKey(): class ECKey():
"""A secp256k1 private key""" """A secp256k1 private key"""
@@ -368,15 +379,18 @@ class ECKey():
ret.compressed = self.compressed ret.compressed = self.compressed
return ret return ret
def sign_ecdsa(self, msg, low_s=True): def sign_ecdsa(self, msg, low_s=True, rfc6979=False):
"""Construct a DER-encoded ECDSA signature with this key. """Construct a DER-encoded ECDSA signature with this key.
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
ECDSA signer algorithm.""" ECDSA signer algorithm."""
assert(self.valid) assert(self.valid)
z = int.from_bytes(msg, 'big') z = int.from_bytes(msg, 'big')
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation) # Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)
k = random.randrange(1, SECP256K1_ORDER) if rfc6979:
k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big')
else:
k = random.randrange(1, SECP256K1_ORDER)
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)])) R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
r = R[0] % SECP256K1_ORDER r = R[0] % SECP256K1_ORDER
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
+58 -26
View File
@@ -619,16 +619,15 @@ def FindAndDelete(script, sig):
r += script[last_sop_idx:] r += script[last_sop_idx:]
return CScript(r) return CScript(r)
def LegacySignatureHash(script, txTo, inIdx, hashtype): def LegacySignatureMsg(script, txTo, inIdx, hashtype):
"""Consensus-correct SignatureHash """Preimage of the signature hash, if it exists.
Returns (hash, err) to precisely match the consensus-critical behavior of Returns either (None, err) to indicate error (which translates to sighash 1),
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity) or (msg, None).
""" """
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
if inIdx >= len(txTo.vin): if inIdx >= len(txTo.vin):
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) return (None, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
txtmp = CTransaction(txTo) txtmp = CTransaction(txTo)
for txin in txtmp.vin: for txin in txtmp.vin:
@@ -645,7 +644,7 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
elif (hashtype & 0x1f) == SIGHASH_SINGLE: elif (hashtype & 0x1f) == SIGHASH_SINGLE:
outIdx = inIdx outIdx = inIdx
if outIdx >= len(txtmp.vout): if outIdx >= len(txtmp.vout):
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) return (None, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
tmp = txtmp.vout[outIdx] tmp = txtmp.vout[outIdx]
txtmp.vout = [] txtmp.vout = []
@@ -665,15 +664,27 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
s = txtmp.serialize_without_witness() s = txtmp.serialize_without_witness()
s += struct.pack(b"<I", hashtype) s += struct.pack(b"<I", hashtype)
hash = hash256(s) return (s, None)
return (hash, None) def LegacySignatureHash(*args, **kwargs):
"""Consensus-correct SignatureHash
Returns (hash, err) to precisely match the consensus-critical behavior of
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
"""
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
msg, err = LegacySignatureMsg(*args, **kwargs)
if msg is None:
return (HASH_ONE, err)
else:
return (hash256(msg), err)
# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided. # TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however. # Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used # Note that this corresponds to sigversion == 1 in EvalScript, which is used
# for version 0 witnesses. # for version 0 witnesses.
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount): def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
hashPrevouts = 0 hashPrevouts = 0
hashSequence = 0 hashSequence = 0
@@ -711,8 +722,10 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
ss += ser_uint256(hashOutputs) ss += ser_uint256(hashOutputs)
ss += struct.pack("<i", txTo.nLockTime) ss += struct.pack("<i", txTo.nLockTime)
ss += struct.pack("<I", hashtype) ss += struct.pack("<I", hashtype)
return ss
return hash256(ss) def SegwitV0SignatureHash(*args, **kwargs):
return hash256(SegwitV0SignatureMsg(*args, **kwargs))
class TestFrameworkScript(unittest.TestCase): class TestFrameworkScript(unittest.TestCase):
def test_bn2vch(self): def test_bn2vch(self):
@@ -742,7 +755,22 @@ class TestFrameworkScript(unittest.TestCase):
for value in values: for value in values:
self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value) self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT): def BIP341_sha_prevouts(txTo):
return sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
def BIP341_sha_amounts(spent_utxos):
return sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos))
def BIP341_sha_scriptpubkeys(spent_utxos):
return sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
def BIP341_sha_sequences(txTo):
return sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
def BIP341_sha_outputs(txTo):
return sha256(b"".join(o.serialize() for o in txTo.vout))
def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
assert (len(txTo.vin) == len(spent_utxos)) assert (len(txTo.vin) == len(spent_utxos))
assert (input_index < len(txTo.vin)) assert (input_index < len(txTo.vin))
out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3 out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
@@ -752,12 +780,12 @@ def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpa
ss += struct.pack("<i", txTo.nVersion) ss += struct.pack("<i", txTo.nVersion)
ss += struct.pack("<I", txTo.nLockTime) ss += struct.pack("<I", txTo.nLockTime)
if in_type != SIGHASH_ANYONECANPAY: if in_type != SIGHASH_ANYONECANPAY:
ss += sha256(b"".join(i.prevout.serialize() for i in txTo.vin)) ss += BIP341_sha_prevouts(txTo)
ss += sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos)) ss += BIP341_sha_amounts(spent_utxos)
ss += sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos)) ss += BIP341_sha_scriptpubkeys(spent_utxos)
ss += sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin)) ss += BIP341_sha_sequences(txTo)
if out_type == SIGHASH_ALL: if out_type == SIGHASH_ALL:
ss += sha256(b"".join(o.serialize() for o in txTo.vout)) ss += BIP341_sha_outputs(txTo)
spend_type = 0 spend_type = 0
if annex is not None: if annex is not None:
spend_type |= 1 spend_type |= 1
@@ -783,7 +811,10 @@ def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpa
ss += bytes([0]) ss += bytes([0])
ss += struct.pack("<i", codeseparator_pos) ss += struct.pack("<i", codeseparator_pos)
assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37 assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
return TaggedHash("TapSighash", ss) return ss
def TaprootSignatureHash(*args, **kwargs):
return TaggedHash("TapSighash", TaprootSignatureMsg(*args, **kwargs))
def taproot_tree_helper(scripts): def taproot_tree_helper(scripts):
if len(scripts) == 0: if len(scripts) == 0:
@@ -805,20 +836,20 @@ def taproot_tree_helper(scripts):
h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code)) h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
if name is None: if name is None:
return ([], h) return ([], h)
return ([(name, version, code, bytes())], h) return ([(name, version, code, bytes(), h)], h)
elif len(scripts) == 2 and callable(scripts[1]): elif len(scripts) == 2 and callable(scripts[1]):
# Two entries, and the right one is a function # Two entries, and the right one is a function
left, left_h = taproot_tree_helper(scripts[0:1]) left, left_h = taproot_tree_helper(scripts[0:1])
right_h = scripts[1](left_h) right_h = scripts[1](left_h)
left = [(name, version, script, control + right_h) for name, version, script, control in left] left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
right = [] right = []
else: else:
# Two or more entries: descend into each side # Two or more entries: descend into each side
split_pos = len(scripts) // 2 split_pos = len(scripts) // 2
left, left_h = taproot_tree_helper(scripts[0:split_pos]) left, left_h = taproot_tree_helper(scripts[0:split_pos])
right, right_h = taproot_tree_helper(scripts[split_pos:]) right, right_h = taproot_tree_helper(scripts[split_pos:])
left = [(name, version, script, control + right_h) for name, version, script, control in left] left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
right = [(name, version, script, control + left_h) for name, version, script, control in right] right = [(name, version, script, control + left_h, leaf) for name, version, script, control, leaf in right]
if right_h < left_h: if right_h < left_h:
right_h, left_h = left_h, right_h right_h, left_h = left_h, right_h
h = TaggedHash("TapBranch", left_h + right_h) h = TaggedHash("TapBranch", left_h + right_h)
@@ -830,13 +861,14 @@ def taproot_tree_helper(scripts):
# - negflag: whether the pubkey in the scriptPubKey was negated from internal_pubkey+tweak*G (bool). # - negflag: whether the pubkey in the scriptPubKey was negated from internal_pubkey+tweak*G (bool).
# - tweak: the tweak (32 bytes) # - tweak: the tweak (32 bytes)
# - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves # - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves
TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves") # - merkle_root: the script tree's Merkle root, or bytes() if no leaves are present
TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tweak,leaves,merkle_root,output_pubkey")
# A TaprootLeafInfo object has the following fields: # A TaprootLeafInfo object has the following fields:
# - script: the leaf script (CScript or bytes) # - script: the leaf script (CScript or bytes)
# - version: the leaf version (0xc0 for BIP342 tapscript) # - version: the leaf version (0xc0 for BIP342 tapscript)
# - merklebranch: the merkle branch to use for this leaf (32*N bytes) # - merklebranch: the merkle branch to use for this leaf (32*N bytes)
TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch") TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch,leaf_hash")
def taproot_construct(pubkey, scripts=None): def taproot_construct(pubkey, scripts=None):
"""Construct a tree of Taproot spending conditions """Construct a tree of Taproot spending conditions
@@ -858,8 +890,8 @@ def taproot_construct(pubkey, scripts=None):
ret, h = taproot_tree_helper(scripts) ret, h = taproot_tree_helper(scripts)
tweak = TaggedHash("TapTweak", pubkey + h) tweak = TaggedHash("TapTweak", pubkey + h)
tweaked, negated = tweak_add_pubkey(pubkey, tweak) tweaked, negated = tweak_add_pubkey(pubkey, tweak)
leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret) leaves = dict((name, TaprootLeafInfo(script, version, merklebranch, leaf)) for name, version, script, merklebranch, leaf in ret)
return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves) return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked)
def is_op_success(o): def is_op_success(o):
return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe) return o == 0x50 or o == 0x62 or o == 0x89 or o == 0x8a or o == 0x8d or o == 0x8e or (o >= 0x7e and o <= 0x81) or (o >= 0x83 and o <= 0x86) or (o >= 0x95 and o <= 0x99) or (o >= 0xbb and o <= 0xfe)