mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-04 13:55:23 -05:00
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 ACKf1c33ee4ac
Tree-SHA512: fcf7109539cb214d3190516b205cd32d2b1b452f14aa66f4107acfaa8bfc7d368f626857f1935665a4342eabc0b9ee8aba608a7c0a2494bec0b498e723439c9d
This commit is contained in:
commit
5ccab7187b
13 changed files with 943 additions and 55 deletions
|
@ -16,6 +16,7 @@ FUZZ_BINARY=test/fuzz/fuzz$(EXEEXT)
|
|||
|
||||
JSON_TEST_FILES = \
|
||||
test/data/script_tests.json \
|
||||
test/data/bip341_wallet_vectors.json \
|
||||
test/data/base58_encode_decode.json \
|
||||
test/data/blockfilters.json \
|
||||
test/data/key_io_valid.json \
|
||||
|
|
|
@ -275,7 +275,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig)
|
|||
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);
|
||||
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);
|
||||
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) {
|
||||
// Additional verification step to prevent using a potentially corrupted signature
|
||||
secp256k1_xonly_pubkey pubkey_verify;
|
||||
|
|
|
@ -130,7 +130,7 @@ public:
|
|||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
|
||||
|
|
|
@ -1483,7 +1483,7 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo,
|
|||
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& 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_TAPBRANCH = TaggedHash("TapBranch");
|
||||
|
||||
|
|
|
@ -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_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_TAPBRANCH; //!< Hasher with tag "TapBranch" pre-fed to it.
|
||||
|
||||
|
|
|
@ -81,7 +81,8 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
|
|||
uint256 hash;
|
||||
if (!SignatureHashSchnorr(hash, execdata, *txTo, nIn, nHashType, sigversion, *m_txdata, MissingDataBehavior::FAIL)) return false;
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
|
452
src/test/data/bip341_wallet_vectors.json
Normal file
452
src/test/data/bip341_wallet_vectors.json
Normal 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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -321,7 +321,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
|
|||
key.Set(sec.begin(), sec.end(), true);
|
||||
XOnlyPubKey pubkey(key.GetPubKey());
|
||||
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(std::vector<unsigned char>(sig64, sig64 + 64) == sig);
|
||||
// Verify those signatures for good measure.
|
||||
|
@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(bip340_test_vectors)
|
|||
BOOST_CHECK(tweaked);
|
||||
XOnlyPubKey tweaked_key = tweaked->first;
|
||||
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(tweaked_key.VerifySchnorr(msg256, sig64));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <test/data/bip341_wallet_vectors.json.h>
|
||||
|
||||
#include <key.h>
|
||||
#include <key_io.h>
|
||||
#include <script/script.h>
|
||||
|
@ -12,6 +14,8 @@
|
|||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
|
||||
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_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()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <test/data/script_tests.json.h>
|
||||
#include <test/data/bip341_wallet_vectors.json.h>
|
||||
|
||||
#include <core_io.h>
|
||||
#include <fs.h>
|
||||
|
@ -1743,4 +1744,79 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
|
|||
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()
|
||||
|
|
|
@ -22,11 +22,17 @@ from test_framework.messages import (
|
|||
)
|
||||
from test_framework.script import (
|
||||
ANNEX_TAG,
|
||||
BIP341_sha_amounts,
|
||||
BIP341_sha_outputs,
|
||||
BIP341_sha_prevouts,
|
||||
BIP341_sha_scriptpubkeys,
|
||||
BIP341_sha_sequences,
|
||||
CScript,
|
||||
CScriptNum,
|
||||
CScriptOp,
|
||||
hash256,
|
||||
LEAF_VERSION_TAPSCRIPT,
|
||||
LegacySignatureHash,
|
||||
LegacySignatureMsg,
|
||||
LOCKTIME_THRESHOLD,
|
||||
MAX_SCRIPT_ELEMENT_SIZE,
|
||||
OP_0,
|
||||
|
@ -70,13 +76,15 @@ from test_framework.script import (
|
|||
SIGHASH_NONE,
|
||||
SIGHASH_SINGLE,
|
||||
SIGHASH_ANYONECANPAY,
|
||||
SegwitV0SignatureHash,
|
||||
TaprootSignatureHash,
|
||||
SegwitV0SignatureMsg,
|
||||
TaggedHash,
|
||||
TaprootSignatureMsg,
|
||||
is_op_success,
|
||||
taproot_construct,
|
||||
)
|
||||
from test_framework.script_util import (
|
||||
key_to_p2pk_script,
|
||||
key_to_p2pkh_script,
|
||||
key_to_p2wpkh_script,
|
||||
keyhash_to_p2pkh_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.address import (
|
||||
hash160,
|
||||
program_to_witness
|
||||
)
|
||||
from collections import OrderedDict, namedtuple
|
||||
from io import BytesIO
|
||||
|
@ -95,6 +104,9 @@ import hashlib
|
|||
import os
|
||||
import random
|
||||
|
||||
# Whether or not to output generated test vectors, in JSON format.
|
||||
GEN_TEST_VECTORS = False
|
||||
|
||||
# === Framework for building spending transactions. ===
|
||||
#
|
||||
# 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."""
|
||||
return bytes([get(ctx, "leafversion") + get(ctx, "negflag")]) + get(ctx, "pubkey_internal") + get(ctx, "merklebranch")
|
||||
|
||||
def default_sighash(ctx):
|
||||
"""Default expression for "sighash": depending on mode, compute BIP341, BIP143, or legacy sighash."""
|
||||
def default_sigmsg(ctx):
|
||||
"""Default expression for "sigmsg": depending on mode, compute BIP341, BIP143, or legacy sigmsg."""
|
||||
tx = get(ctx, "tx")
|
||||
idx = get(ctx, "idx")
|
||||
hashtype = get(ctx, "hashtype_actual")
|
||||
|
@ -208,18 +220,30 @@ def default_sighash(ctx):
|
|||
codeseppos = get(ctx, "codeseppos")
|
||||
leaf_ver = get(ctx, "leafversion")
|
||||
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:
|
||||
return TaprootSignatureHash(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
|
||||
return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
|
||||
elif mode == "witv0":
|
||||
# BIP143 signature hash
|
||||
scriptcode = get(ctx, "scriptcode")
|
||||
utxos = get(ctx, "utxos")
|
||||
return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue)
|
||||
return SegwitV0SignatureMsg(scriptcode, tx, idx, hashtype, utxos[idx].nValue)
|
||||
else:
|
||||
# Pre-segwit signature hash
|
||||
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):
|
||||
"""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):
|
||||
"""Default expression for "signature": BIP340 signature or ECDSA signature depending on mode."""
|
||||
sighash = get(ctx, "sighash")
|
||||
deterministic = get(ctx, "deterministic")
|
||||
if get(ctx, "mode") == "taproot":
|
||||
key = get(ctx, "key_tweaked")
|
||||
flip_r = get(ctx, "flag_flip_r")
|
||||
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:
|
||||
key = get(ctx, "key")
|
||||
return key.sign_ecdsa(sighash)
|
||||
return key.sign_ecdsa(sighash, rfc6979=deterministic)
|
||||
|
||||
def default_hashtype_actual(ctx):
|
||||
"""Default expression for "hashtype_actual": hashtype, unless mismatching SIGHASH_SINGLE in taproot."""
|
||||
|
@ -340,6 +368,8 @@ DEFAULT_CONTEXT = {
|
|||
"key_tweaked": default_key_tweaked,
|
||||
# The tweak to use (None for script path spends, the actual tweak for key path spends).
|
||||
"tweak": default_tweak,
|
||||
# The sigmsg value (preimage of sighash)
|
||||
"sigmsg": default_sigmsg,
|
||||
# The sighash value (32 bytes)
|
||||
"sighash": default_sighash,
|
||||
# The information about the chosen script path spend (TaprootLeafInfo object).
|
||||
|
@ -376,6 +406,8 @@ DEFAULT_CONTEXT = {
|
|||
"leaf": None,
|
||||
# The input arguments to provide to the executed script
|
||||
"inputs": [],
|
||||
# Use deterministic signing nonces
|
||||
"deterministic": False,
|
||||
|
||||
# == Parameters to be set before evaluation: ==
|
||||
# - mode: what spending style to use ("taproot", "witv0", or "legacy").
|
||||
|
@ -396,6 +428,7 @@ def flatten(lst):
|
|||
ret.append(elem)
|
||||
return ret
|
||||
|
||||
|
||||
def spend(tx, idx, utxos, **kwargs):
|
||||
"""Sign transaction input idx of tx, provided utxos is the list of outputs being spent.
|
||||
|
||||
|
@ -1253,6 +1286,14 @@ class TaprootTest(BitcoinTestFramework):
|
|||
else:
|
||||
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):
|
||||
"""Run randomized tests with a number of "spenders".
|
||||
|
||||
|
@ -1279,12 +1320,7 @@ class TaprootTest(BitcoinTestFramework):
|
|||
host_spks.append(spk)
|
||||
host_pubkeys.append(bytes.fromhex(info['pubkey']))
|
||||
|
||||
# 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']
|
||||
self.init_blockinfo(node)
|
||||
|
||||
# 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
|
||||
|
@ -1458,10 +1494,239 @@ class TaprootTest(BitcoinTestFramework):
|
|||
assert len(mismatching_utxos) == 0
|
||||
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):
|
||||
self.gen_test_vectors()
|
||||
|
||||
# Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
|
||||
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])
|
||||
|
||||
# Re-connect nodes in case they have been disconnected
|
||||
|
|
|
@ -8,6 +8,7 @@ keys, and is trivially vulnerable to side channel attacks. Do not use for
|
|||
anything but tests."""
|
||||
import csv
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
import random
|
||||
import unittest
|
||||
|
@ -326,6 +327,16 @@ def generate_privkey():
|
|||
"""Generate a valid random 32-byte private key."""
|
||||
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():
|
||||
"""A secp256k1 private key"""
|
||||
|
||||
|
@ -368,15 +379,18 @@ class ECKey():
|
|||
ret.compressed = self.compressed
|
||||
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.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||
ECDSA signer algorithm."""
|
||||
assert(self.valid)
|
||||
z = int.from_bytes(msg, 'big')
|
||||
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
||||
k = random.randrange(1, SECP256K1_ORDER)
|
||||
# Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
||||
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 = R[0] % SECP256K1_ORDER
|
||||
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
||||
|
|
|
@ -619,16 +619,15 @@ def FindAndDelete(script, sig):
|
|||
r += script[last_sop_idx:]
|
||||
return CScript(r)
|
||||
|
||||
def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
||||
"""Consensus-correct SignatureHash
|
||||
def LegacySignatureMsg(script, txTo, inIdx, hashtype):
|
||||
"""Preimage of the signature hash, if it exists.
|
||||
|
||||
Returns (hash, err) to precisely match the consensus-critical behavior of
|
||||
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
|
||||
Returns either (None, err) to indicate error (which translates to sighash 1),
|
||||
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):
|
||||
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)
|
||||
|
||||
for txin in txtmp.vin:
|
||||
|
@ -645,7 +644,7 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
|||
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
|
||||
outIdx = inIdx
|
||||
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]
|
||||
txtmp.vout = []
|
||||
|
@ -665,15 +664,27 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
|||
s = txtmp.serialize_without_witness()
|
||||
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.
|
||||
# Performance optimization probably not necessary for python tests, however.
|
||||
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
|
||||
# for version 0 witnesses.
|
||||
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
|
||||
def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
|
||||
|
||||
hashPrevouts = 0
|
||||
hashSequence = 0
|
||||
|
@ -711,8 +722,10 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
ss += ser_uint256(hashOutputs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
return ss
|
||||
|
||||
return hash256(ss)
|
||||
def SegwitV0SignatureHash(*args, **kwargs):
|
||||
return hash256(SegwitV0SignatureMsg(*args, **kwargs))
|
||||
|
||||
class TestFrameworkScript(unittest.TestCase):
|
||||
def test_bn2vch(self):
|
||||
|
@ -742,7 +755,22 @@ class TestFrameworkScript(unittest.TestCase):
|
|||
for value in values:
|
||||
self.assertEqual(CScriptNum.decode(CScriptNum.encode(CScriptNum(value))), value)
|
||||
|
||||
def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
|
||||
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 (input_index < len(txTo.vin))
|
||||
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.nLockTime)
|
||||
if in_type != SIGHASH_ANYONECANPAY:
|
||||
ss += sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
|
||||
ss += sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos))
|
||||
ss += sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
|
||||
ss += sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
|
||||
ss += BIP341_sha_prevouts(txTo)
|
||||
ss += BIP341_sha_amounts(spent_utxos)
|
||||
ss += BIP341_sha_scriptpubkeys(spent_utxos)
|
||||
ss += BIP341_sha_sequences(txTo)
|
||||
if out_type == SIGHASH_ALL:
|
||||
ss += sha256(b"".join(o.serialize() for o in txTo.vout))
|
||||
ss += BIP341_sha_outputs(txTo)
|
||||
spend_type = 0
|
||||
if annex is not None:
|
||||
spend_type |= 1
|
||||
|
@ -783,7 +811,10 @@ def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpa
|
|||
ss += bytes([0])
|
||||
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
|
||||
return TaggedHash("TapSighash", ss)
|
||||
return ss
|
||||
|
||||
def TaprootSignatureHash(*args, **kwargs):
|
||||
return TaggedHash("TapSighash", TaprootSignatureMsg(*args, **kwargs))
|
||||
|
||||
def taproot_tree_helper(scripts):
|
||||
if len(scripts) == 0:
|
||||
|
@ -805,20 +836,20 @@ def taproot_tree_helper(scripts):
|
|||
h = TaggedHash("TapLeaf", bytes([version]) + ser_string(code))
|
||||
if name is None:
|
||||
return ([], h)
|
||||
return ([(name, version, code, bytes())], h)
|
||||
return ([(name, version, code, bytes(), h)], h)
|
||||
elif len(scripts) == 2 and callable(scripts[1]):
|
||||
# Two entries, and the right one is a function
|
||||
left, left_h = taproot_tree_helper(scripts[0:1])
|
||||
right_h = scripts[1](left_h)
|
||||
left = [(name, version, script, control + right_h) for name, version, script, control in left]
|
||||
left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
|
||||
right = []
|
||||
else:
|
||||
# Two or more entries: descend into each side
|
||||
split_pos = len(scripts) // 2
|
||||
left, left_h = taproot_tree_helper(scripts[0:split_pos])
|
||||
right, right_h = taproot_tree_helper(scripts[split_pos:])
|
||||
left = [(name, version, script, control + right_h) for name, version, script, control in left]
|
||||
right = [(name, version, script, control + left_h) for name, version, script, control in right]
|
||||
left = [(name, version, script, control + right_h, leaf) for name, version, script, control, leaf in left]
|
||||
right = [(name, version, script, control + left_h, leaf) for name, version, script, control, leaf in right]
|
||||
if right_h < left_h:
|
||||
right_h, left_h = 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).
|
||||
# - tweak: the tweak (32 bytes)
|
||||
# - 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:
|
||||
# - script: the leaf script (CScript or bytes)
|
||||
# - version: the leaf version (0xc0 for BIP342 tapscript)
|
||||
# - 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):
|
||||
"""Construct a tree of Taproot spending conditions
|
||||
|
@ -858,8 +890,8 @@ def taproot_construct(pubkey, scripts=None):
|
|||
ret, h = taproot_tree_helper(scripts)
|
||||
tweak = TaggedHash("TapTweak", pubkey + h)
|
||||
tweaked, negated = tweak_add_pubkey(pubkey, tweak)
|
||||
leaves = dict((name, TaprootLeafInfo(script, version, merklebranch)) for name, version, script, merklebranch in ret)
|
||||
return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves)
|
||||
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, h, tweaked)
|
||||
|
||||
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)
|
||||
|
|
Loading…
Add table
Reference in a new issue