mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Compare commits
14 commits
6a60a325bf
...
296ea00e55
Author | SHA1 | Date | |
---|---|---|---|
|
296ea00e55 | ||
|
65714c162c | ||
|
a4eee6d50b | ||
|
af6cffa36d | ||
|
33e6538b30 | ||
|
fa655da159 | ||
|
fa55dd01df | ||
|
fa6f77ed3c | ||
|
fa48be3ba4 | ||
|
aaaa45399c | ||
|
cccc1f4e91 | ||
|
c7e2b9e264 | ||
|
fa58f40b89 | ||
|
fadf12a56c |
10 changed files with 135 additions and 87 deletions
|
@ -1423,16 +1423,16 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
|
||||||
apostrophe = last == '\'';
|
apostrophe = last == '\'';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32_t p;
|
const auto p{ToIntegral<uint32_t>(std::string_view{elem.begin(), elem.end()})};
|
||||||
if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) {
|
if (!p) {
|
||||||
error = strprintf("Key path value '%s' is not a valid uint32", std::string(elem.begin(), elem.end()));
|
error = strprintf("Key path value '%s' is not a valid uint32", std::string_view{elem.begin(), elem.end()});
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else if (p > 0x7FFFFFFFUL) {
|
} else if (*p > 0x7FFFFFFFUL) {
|
||||||
error = strprintf("Key path value %u is out of range", p);
|
error = strprintf("Key path value %u is out of range", *p);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_optional<uint32_t>(p | (((uint32_t)hardened) << 31));
|
return std::make_optional<uint32_t>(*p | (((uint32_t)hardened) << 31));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1821,7 +1821,9 @@ std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index
|
||||||
auto threshold = Expr(expr);
|
auto threshold = Expr(expr);
|
||||||
uint32_t thres;
|
uint32_t thres;
|
||||||
std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers; // List of multipath expanded pubkeys
|
std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers; // List of multipath expanded pubkeys
|
||||||
if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) {
|
if (const auto maybe_thres{ToIntegral<uint32_t>(std::string_view{threshold.begin(), threshold.end()})}) {
|
||||||
|
thres = *maybe_thres;
|
||||||
|
} else {
|
||||||
error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()));
|
error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <script/descriptor.h>
|
#include <script/descriptor.h>
|
||||||
#include <script/sign.h>
|
#include <script/sign.h>
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
|
#include <util/check.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/string.h>
|
#include <util/string.h>
|
||||||
|
|
||||||
|
@ -496,8 +497,7 @@ void CheckInferDescriptor(const std::string& script_hex, const std::string& expe
|
||||||
hardened = true;
|
hardened = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32_t p;
|
const uint32_t p{*Assert(ToIntegral<uint32_t>(std::string_view{elem.begin(), elem.end()}))};
|
||||||
assert(ParseUInt32(std::string(elem.begin(), elem.end()), &p));
|
|
||||||
info.path.push_back(p | (((uint32_t)hardened) << 31));
|
info.path.push_back(p | (((uint32_t)hardened) << 31));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,6 +567,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
|
||||||
CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo(): Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
|
CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", "combo(): Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long key fingerprint
|
||||||
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "pkh(): Key path value 2147483648 is out of range"); // BIP 32 path element overflow
|
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)", "pkh(): Key path value 2147483648 is out of range"); // BIP 32 path element overflow
|
||||||
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "pkh(): Key path value '1aa' is not a valid uint32"); // Path is not valid uint
|
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/1aa)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1aa)", "pkh(): Key path value '1aa' is not a valid uint32"); // Path is not valid uint
|
||||||
|
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/+1)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/+1)", "pkh(): Key path value '+1' is not a valid uint32"); // Path is not valid uint
|
||||||
Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{10, 20, 0xFFFFFFFFUL, 0}});
|
Check("pkh([01234567/10/20]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh([01234567/10/20]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", "pkh([01234567/10/20/2147483647h]xpub69H7F5dQzmVd3vPuLKtcXJziMEQByuDidnX3YdwgtNsecY5HRGtAAQC5mXTt4dsv9RzyjgDjAQs9VGVV6ydYCHnprc9vvaA5YtqWyL6hyds/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, OutputType::LEGACY, /*op_desc_id=*/std::nullopt, {{10, 20, 0xFFFFFFFFUL, 0}});
|
||||||
|
|
||||||
// Multipath versions with BIP32 derivations
|
// Multipath versions with BIP32 derivations
|
||||||
|
@ -923,6 +924,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
|
||||||
CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint
|
CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (7 characters instead of 8 characters)"); // Too short fingerprint
|
||||||
CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint
|
CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647h/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*h))", "Multi: Fingerprint is not 4 bytes (9 characters instead of 8 characters)"); // Too long fingerprint
|
||||||
CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold
|
CheckUnparsable("multi(a,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(a,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold 'a' is not valid"); // Invalid threshold
|
||||||
|
CheckUnparsable("multi(+1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(+1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multi threshold '+1' is not valid"); // Invalid threshold
|
||||||
CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
|
CheckUnparsable("multi(0,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(0,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be 0, must be at least 1"); // Threshold of 0
|
||||||
CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
|
CheckUnparsable("multi(3,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(3,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", "Multisig threshold cannot be larger than the number of keys; threshold is 3 but only 2 keys specified"); // Threshold larger than number of keys
|
||||||
CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
|
CheckUnparsable("multi(3,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f)", "multi(3,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8)", "Cannot have 4 pubkeys in bare multisig; only at most 3 pubkeys"); // Threshold larger than number of keys
|
||||||
|
|
|
@ -111,7 +111,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
# Mine a transaction that credits the offline address
|
# Mine a transaction that credits the offline address
|
||||||
offline_addr = offline_node.getnewaddress(address_type="bech32m")
|
offline_addr = offline_node.getnewaddress(address_type="bech32m")
|
||||||
online_addr = w2.getnewaddress(address_type="bech32m")
|
online_addr = w2.getnewaddress(address_type="bech32m")
|
||||||
wonline.importaddress(offline_addr, "", False)
|
wonline.importaddress(offline_addr, label="", rescan=False)
|
||||||
mining_wallet = mining_node.get_wallet_rpc(self.default_wallet_name)
|
mining_wallet = mining_node.get_wallet_rpc(self.default_wallet_name)
|
||||||
mining_wallet.sendtoaddress(address=offline_addr, amount=1.0)
|
mining_wallet.sendtoaddress(address=offline_addr, amount=1.0)
|
||||||
self.generate(mining_node, nblocks=1, sync_fun=lambda: self.sync_all([online_node, mining_node]))
|
self.generate(mining_node, nblocks=1, sync_fun=lambda: self.sync_all([online_node, mining_node]))
|
||||||
|
@ -312,9 +312,9 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
wmulti = self.nodes[2].get_wallet_rpc('wmulti')
|
wmulti = self.nodes[2].get_wallet_rpc('wmulti')
|
||||||
|
|
||||||
# Create all the addresses
|
# Create all the addresses
|
||||||
p2sh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "legacy")['address']
|
p2sh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="legacy")["address"]
|
||||||
p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "bech32")['address']
|
p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="bech32")["address"]
|
||||||
p2sh_p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], "", "p2sh-segwit")['address']
|
p2sh_p2wsh = wmulti.addmultisigaddress(2, [pubkey0, pubkey1, pubkey2], label="", address_type="p2sh-segwit")["address"]
|
||||||
p2wpkh = self.nodes[1].getnewaddress("", "bech32")
|
p2wpkh = self.nodes[1].getnewaddress("", "bech32")
|
||||||
p2pkh = self.nodes[1].getnewaddress("", "legacy")
|
p2pkh = self.nodes[1].getnewaddress("", "legacy")
|
||||||
p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
p2sh_p2wpkh = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2017-2022 The Bitcoin Core developers
|
# Copyright (c) 2017-present The Bitcoin Core developers
|
||||||
# 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.
|
||||||
"""Class for bitcoind node under test"""
|
"""Class for bitcoind node under test"""
|
||||||
|
@ -209,7 +209,7 @@ class TestNode():
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
|
"""Dispatches any unrecognised messages to the RPC connection or a CLI instance."""
|
||||||
if self.use_cli:
|
if self.use_cli:
|
||||||
return getattr(RPCOverloadWrapper(self.cli, True), name)
|
return getattr(RPCOverloadWrapper(self.cli), name)
|
||||||
else:
|
else:
|
||||||
assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection")
|
assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection")
|
||||||
return getattr(RPCOverloadWrapper(self.rpc), name)
|
return getattr(RPCOverloadWrapper(self.rpc), name)
|
||||||
|
@ -374,7 +374,7 @@ class TestNode():
|
||||||
|
|
||||||
def get_wallet_rpc(self, wallet_name):
|
def get_wallet_rpc(self, wallet_name):
|
||||||
if self.use_cli:
|
if self.use_cli:
|
||||||
return RPCOverloadWrapper(self.cli("-rpcwallet={}".format(wallet_name)), True)
|
return RPCOverloadWrapper(self.cli("-rpcwallet={}".format(wallet_name)))
|
||||||
else:
|
else:
|
||||||
assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected")
|
assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected")
|
||||||
wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name))
|
wallet_path = "wallet/{}".format(urllib.parse.quote(wallet_name))
|
||||||
|
@ -925,17 +925,13 @@ class TestNodeCLI():
|
||||||
return cli_stdout.rstrip("\n")
|
return cli_stdout.rstrip("\n")
|
||||||
|
|
||||||
class RPCOverloadWrapper():
|
class RPCOverloadWrapper():
|
||||||
def __init__(self, rpc, cli=False):
|
def __init__(self, rpc):
|
||||||
self.rpc = rpc
|
self.rpc = rpc
|
||||||
self.is_cli = cli
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self.rpc, name)
|
return getattr(self.rpc, name)
|
||||||
|
|
||||||
def createwallet_passthrough(self, *args, **kwargs):
|
def importprivkey(self, privkey, *, label=None, rescan=None):
|
||||||
return self.__getattr__("createwallet")(*args, **kwargs)
|
|
||||||
|
|
||||||
def importprivkey(self, privkey, label=None, rescan=None):
|
|
||||||
wallet_info = self.getwalletinfo()
|
wallet_info = self.getwalletinfo()
|
||||||
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
||||||
return self.__getattr__('importprivkey')(privkey, label, rescan)
|
return self.__getattr__('importprivkey')(privkey, label, rescan)
|
||||||
|
@ -943,13 +939,13 @@ class RPCOverloadWrapper():
|
||||||
req = [{
|
req = [{
|
||||||
'desc': desc,
|
'desc': desc,
|
||||||
'timestamp': 0 if rescan else 'now',
|
'timestamp': 0 if rescan else 'now',
|
||||||
'label': label if label else ''
|
'label': label if label else '',
|
||||||
}]
|
}]
|
||||||
import_res = self.importdescriptors(req)
|
import_res = self.importdescriptors(req)
|
||||||
if not import_res[0]['success']:
|
if not import_res[0]['success']:
|
||||||
raise JSONRPCException(import_res[0]['error'])
|
raise JSONRPCException(import_res[0]['error'])
|
||||||
|
|
||||||
def addmultisigaddress(self, nrequired, keys, label=None, address_type=None):
|
def addmultisigaddress(self, nrequired, keys, *, label=None, address_type=None):
|
||||||
wallet_info = self.getwalletinfo()
|
wallet_info = self.getwalletinfo()
|
||||||
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
||||||
return self.__getattr__('addmultisigaddress')(nrequired, keys, label, address_type)
|
return self.__getattr__('addmultisigaddress')(nrequired, keys, label, address_type)
|
||||||
|
@ -957,14 +953,14 @@ class RPCOverloadWrapper():
|
||||||
req = [{
|
req = [{
|
||||||
'desc': cms['descriptor'],
|
'desc': cms['descriptor'],
|
||||||
'timestamp': 0,
|
'timestamp': 0,
|
||||||
'label': label if label else ''
|
'label': label if label else '',
|
||||||
}]
|
}]
|
||||||
import_res = self.importdescriptors(req)
|
import_res = self.importdescriptors(req)
|
||||||
if not import_res[0]['success']:
|
if not import_res[0]['success']:
|
||||||
raise JSONRPCException(import_res[0]['error'])
|
raise JSONRPCException(import_res[0]['error'])
|
||||||
return cms
|
return cms
|
||||||
|
|
||||||
def importpubkey(self, pubkey, label=None, rescan=None):
|
def importpubkey(self, pubkey, *, label=None, rescan=None):
|
||||||
wallet_info = self.getwalletinfo()
|
wallet_info = self.getwalletinfo()
|
||||||
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
||||||
return self.__getattr__('importpubkey')(pubkey, label, rescan)
|
return self.__getattr__('importpubkey')(pubkey, label, rescan)
|
||||||
|
@ -972,13 +968,13 @@ class RPCOverloadWrapper():
|
||||||
req = [{
|
req = [{
|
||||||
'desc': desc,
|
'desc': desc,
|
||||||
'timestamp': 0 if rescan else 'now',
|
'timestamp': 0 if rescan else 'now',
|
||||||
'label': label if label else ''
|
'label': label if label else '',
|
||||||
}]
|
}]
|
||||||
import_res = self.importdescriptors(req)
|
import_res = self.importdescriptors(req)
|
||||||
if not import_res[0]['success']:
|
if not import_res[0]['success']:
|
||||||
raise JSONRPCException(import_res[0]['error'])
|
raise JSONRPCException(import_res[0]['error'])
|
||||||
|
|
||||||
def importaddress(self, address, label=None, rescan=None, p2sh=None):
|
def importaddress(self, address, *, label=None, rescan=None, p2sh=None):
|
||||||
wallet_info = self.getwalletinfo()
|
wallet_info = self.getwalletinfo()
|
||||||
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
if 'descriptors' not in wallet_info or ('descriptors' in wallet_info and not wallet_info['descriptors']):
|
||||||
return self.__getattr__('importaddress')(address, label, rescan, p2sh)
|
return self.__getattr__('importaddress')(address, label, rescan, p2sh)
|
||||||
|
@ -992,13 +988,13 @@ class RPCOverloadWrapper():
|
||||||
reqs = [{
|
reqs = [{
|
||||||
'desc': desc,
|
'desc': desc,
|
||||||
'timestamp': 0 if rescan else 'now',
|
'timestamp': 0 if rescan else 'now',
|
||||||
'label': label if label else ''
|
'label': label if label else '',
|
||||||
}]
|
}]
|
||||||
if is_hex and p2sh:
|
if is_hex and p2sh:
|
||||||
reqs.append({
|
reqs.append({
|
||||||
'desc': descsum_create('p2sh(raw(' + address + '))'),
|
'desc': descsum_create('p2sh(raw(' + address + '))'),
|
||||||
'timestamp': 0 if rescan else 'now',
|
'timestamp': 0 if rescan else 'now',
|
||||||
'label': label if label else ''
|
'label': label if label else '',
|
||||||
})
|
})
|
||||||
import_res = self.importdescriptors(reqs)
|
import_res = self.importdescriptors(reqs)
|
||||||
for res in import_res:
|
for res in import_res:
|
||||||
|
|
|
@ -344,7 +344,7 @@ class AddressTypeTest(BitcoinTestFramework):
|
||||||
self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32')
|
self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32')
|
||||||
|
|
||||||
self.log.info('test invalid address type arguments')
|
self.log.info('test invalid address type arguments')
|
||||||
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], None, '')
|
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], address_type="")
|
||||||
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '')
|
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '')
|
||||||
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '')
|
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '')
|
||||||
assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23')
|
assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23')
|
||||||
|
|
|
@ -26,11 +26,12 @@ from test_framework.util import (
|
||||||
assert_raises_rpc_error,
|
assert_raises_rpc_error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
LAST_KEYPOOL_INDEX = 9 # Index of the last derived address with the keypool size of 10
|
||||||
|
|
||||||
class BackwardsCompatibilityTest(BitcoinTestFramework):
|
class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
self.num_nodes = 10
|
self.num_nodes = 8
|
||||||
# Add new version after each release:
|
# Add new version after each release:
|
||||||
self.extra_args = [
|
self.extra_args = [
|
||||||
["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
|
["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
|
||||||
|
@ -38,11 +39,9 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v25.0
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v25.0
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v24.0.1
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v24.0.1
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1", f"-keypool={LAST_KEYPOOL_INDEX + 1}"], # v22.0
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.21.0
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.21.0
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.20.1
|
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.20.1
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.19.1
|
|
||||||
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=127.0.0.1"], # v0.18.1
|
|
||||||
]
|
]
|
||||||
self.wallet_names = [self.default_wallet_name]
|
self.wallet_names = [self.default_wallet_name]
|
||||||
|
|
||||||
|
@ -60,8 +59,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
220000,
|
220000,
|
||||||
210000,
|
210000,
|
||||||
200100,
|
200100,
|
||||||
190100,
|
|
||||||
180100,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
self.start_nodes()
|
self.start_nodes()
|
||||||
|
@ -85,21 +82,98 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
node_major, _, _ = self.split_version(node)
|
node_major, _, _ = self.split_version(node)
|
||||||
return node_major >= major
|
return node_major >= major
|
||||||
|
|
||||||
|
def test_v22_inactivehdchain_path(self):
|
||||||
|
self.log.info("Testing inactive hd chain bad derivation path cleanup")
|
||||||
|
# 0.21.x and 22.x would both produce bad derivation paths when topping up an inactive hd chain
|
||||||
|
# Make sure that this is being automatically cleaned up by migration
|
||||||
|
node_master = self.nodes[1]
|
||||||
|
node_v22 = self.nodes[self.num_nodes - 5]
|
||||||
|
wallet_name = "bad_deriv_path"
|
||||||
|
node_v22.createwallet(wallet_name=wallet_name, descriptors=False)
|
||||||
|
bad_deriv_wallet = node_v22.get_wallet_rpc(wallet_name)
|
||||||
|
|
||||||
|
# Make a dump of the wallet to get an unused address
|
||||||
|
dump_path = node_v22.wallets_path / f"{wallet_name}.dump"
|
||||||
|
bad_deriv_wallet.dumpwallet(dump_path)
|
||||||
|
addr = None
|
||||||
|
seed = None
|
||||||
|
with open(dump_path, encoding="utf8") as f:
|
||||||
|
for line in f:
|
||||||
|
if f"hdkeypath=m/0'/0'/{LAST_KEYPOOL_INDEX}'" in line:
|
||||||
|
addr = line.split(" ")[4].split("=")[1]
|
||||||
|
elif " hdseed=1 " in line:
|
||||||
|
seed = line.split(" ")[0]
|
||||||
|
assert addr is not None
|
||||||
|
assert seed is not None
|
||||||
|
# Rotate seed and unload
|
||||||
|
bad_deriv_wallet.sethdseed()
|
||||||
|
bad_deriv_wallet.unloadwallet()
|
||||||
|
# Receive at addr to trigger inactive chain topup on next load
|
||||||
|
self.nodes[0].sendtoaddress(addr, 1)
|
||||||
|
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
|
||||||
|
self.sync_all(nodes=[self.nodes[0], node_master, node_v22])
|
||||||
|
node_v22.loadwallet(wallet_name)
|
||||||
|
|
||||||
|
# Dump again to find bad hd keypath
|
||||||
|
bad_deriv_path = f"m/0'/0'/{LAST_KEYPOOL_INDEX}'/0'/0'/{LAST_KEYPOOL_INDEX + 1}'"
|
||||||
|
good_deriv_path = f"m/0h/0h/{LAST_KEYPOOL_INDEX + 1}h"
|
||||||
|
os.unlink(dump_path)
|
||||||
|
bad_deriv_wallet.dumpwallet(dump_path)
|
||||||
|
bad_path_addr = None
|
||||||
|
with open(dump_path, encoding="utf8") as f:
|
||||||
|
for line in f:
|
||||||
|
if f"hdkeypath={bad_deriv_path}" in line:
|
||||||
|
bad_path_addr = line.split(" ")[4].split("=")[1]
|
||||||
|
assert bad_path_addr is not None
|
||||||
|
assert_equal(bad_deriv_wallet.getaddressinfo(bad_path_addr)["hdkeypath"], bad_deriv_path)
|
||||||
|
|
||||||
|
# Verify that this bad derivation path addr is actually at m/0'/0'/10' by making a new wallet with the same seed but larger keypool
|
||||||
|
node_v22.createwallet(wallet_name="path_verify", descriptors=False, blank=True)
|
||||||
|
verify_wallet = node_v22.get_wallet_rpc("path_verify")
|
||||||
|
verify_wallet.sethdseed(True, seed)
|
||||||
|
# Bad addr is after keypool, so need to generate it by refilling
|
||||||
|
verify_wallet.keypoolrefill(LAST_KEYPOOL_INDEX + 2)
|
||||||
|
assert_equal(verify_wallet.getaddressinfo(bad_path_addr)["hdkeypath"], good_deriv_path.replace("h", "'"))
|
||||||
|
|
||||||
|
# Migrate with master
|
||||||
|
# Since all keymeta records are now deleted after migration, the derivation path
|
||||||
|
# should now be correct as it is derived on-the-fly from the inactive hd chain's descriptor
|
||||||
|
backup_path = node_v22.wallets_path / f"{wallet_name}.bak"
|
||||||
|
bad_deriv_wallet.backupwallet(backup_path)
|
||||||
|
wallet_dir_master = os.path.join(node_master.wallets_path, wallet_name)
|
||||||
|
os.makedirs(wallet_dir_master, exist_ok=True)
|
||||||
|
shutil.copy(backup_path, os.path.join(wallet_dir_master, "wallet.dat"))
|
||||||
|
node_master.migratewallet(wallet_name)
|
||||||
|
bad_deriv_wallet_master = node_master.get_wallet_rpc(wallet_name)
|
||||||
|
assert_equal(bad_deriv_wallet_master.getaddressinfo(bad_path_addr)["hdkeypath"], good_deriv_path)
|
||||||
|
bad_deriv_wallet_master.unloadwallet()
|
||||||
|
|
||||||
|
# If we have sqlite3, verify that there are no keymeta records
|
||||||
|
try:
|
||||||
|
import sqlite3
|
||||||
|
wallet_db = node_master.wallets_path / wallet_name / "wallet.dat"
|
||||||
|
conn = sqlite3.connect(wallet_db)
|
||||||
|
with conn:
|
||||||
|
# Retrieve all records that have the "keymeta" prefix. The remaining key data varies for each record.
|
||||||
|
keymeta_rec = conn.execute("SELECT value FROM main where key >= x'076b65796d657461' AND key < x'076b65796d657462'").fetchone()
|
||||||
|
assert_equal(keymeta_rec, None)
|
||||||
|
conn.close()
|
||||||
|
except ImportError:
|
||||||
|
self.log.warning("sqlite3 module not available, skipping lack of keymeta records check")
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
node_miner = self.nodes[0]
|
node_miner = self.nodes[0]
|
||||||
node_master = self.nodes[1]
|
node_master = self.nodes[1]
|
||||||
node_v21 = self.nodes[self.num_nodes - 4]
|
node_v21 = self.nodes[self.num_nodes - 2]
|
||||||
node_v18 = self.nodes[self.num_nodes - 1]
|
node_v20 = self.nodes[self.num_nodes - 1] # bdb only
|
||||||
|
|
||||||
legacy_nodes = self.nodes[2:] # Nodes that support legacy wallets
|
legacy_nodes = self.nodes[2:] # Nodes that support legacy wallets
|
||||||
legacy_only_nodes = self.nodes[-3:] # Nodes that only support legacy wallets
|
descriptors_nodes = self.nodes[2:-1] # Nodes that support descriptor wallets
|
||||||
descriptors_nodes = self.nodes[2:-3] # Nodes that support descriptor wallets
|
|
||||||
|
|
||||||
self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress())
|
self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress())
|
||||||
|
|
||||||
# Sanity check the test framework:
|
# Sanity check the test framework:
|
||||||
res = node_v18.getblockchaininfo()
|
assert_equal(node_v20.getblockchaininfo()["blocks"], COINBASE_MATURITY + 1)
|
||||||
assert_equal(res['blocks'], COINBASE_MATURITY + 1)
|
|
||||||
|
|
||||||
self.log.info("Test wallet backwards compatibility...")
|
self.log.info("Test wallet backwards compatibility...")
|
||||||
# Create a number of wallets and open them in older versions:
|
# Create a number of wallets and open them in older versions:
|
||||||
|
@ -206,13 +280,11 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check that descriptor wallets don't work on legacy only nodes
|
# Check that descriptor wallets don't work on legacy only nodes
|
||||||
self.log.info("Test descriptor wallet incompatibility on:")
|
self.log.info("Test descriptor wallet incompatibility on v0.20")
|
||||||
for node in legacy_only_nodes:
|
|
||||||
self.log.info(f"- {node.version}")
|
|
||||||
# Descriptor wallets appear to be corrupted wallets to old software
|
# Descriptor wallets appear to be corrupted wallets to old software
|
||||||
assert self.major_version_less_than(node, 21)
|
assert self.major_version_equals(node_v20, 20)
|
||||||
for wallet_name in ["w1", "w2", "w3"]:
|
for wallet_name in ["w1", "w2", "w3"]:
|
||||||
assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
|
assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v20.loadwallet, wallet_name)
|
||||||
|
|
||||||
# w1 cannot be opened by 0.21 since it contains a taproot descriptor
|
# w1 cannot be opened by 0.21 since it contains a taproot descriptor
|
||||||
self.log.info("Test that 0.21 cannot open wallet containing tr() descriptors")
|
self.log.info("Test that 0.21 cannot open wallet containing tr() descriptors")
|
||||||
|
@ -308,5 +380,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
|
||||||
# Legacy wallets are no longer supported. Trying to load these should result in an error
|
# Legacy wallets are no longer supported. Trying to load these should result in an error
|
||||||
assert_raises_rpc_error(-18, "The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC)", node_master.restorewallet, wallet_name, backup_path)
|
assert_raises_rpc_error(-18, "The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC)", node_master.restorewallet, wallet_name, backup_path)
|
||||||
|
|
||||||
|
self.test_v22_inactivehdchain_path()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
BackwardsCompatibilityTest(__file__).main()
|
BackwardsCompatibilityTest(__file__).main()
|
||||||
|
|
|
@ -192,7 +192,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||||
watchonly_address = self.nodes[0].getnewaddress()
|
watchonly_address = self.nodes[0].getnewaddress()
|
||||||
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
|
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
|
||||||
self.watchonly_amount = Decimal(200)
|
self.watchonly_amount = Decimal(200)
|
||||||
wwatch.importpubkey(watchonly_pubkey, "", True)
|
wwatch.importpubkey(watchonly_pubkey, label="", rescan=True)
|
||||||
self.watchonly_utxo = self.create_outpoints(self.nodes[0], outputs=[{watchonly_address: self.watchonly_amount}])[0]
|
self.watchonly_utxo = self.create_outpoints(self.nodes[0], outputs=[{watchonly_address: self.watchonly_amount}])[0]
|
||||||
|
|
||||||
# Lock UTXO so nodes[0] doesn't accidentally spend it
|
# Lock UTXO so nodes[0] doesn't accidentally spend it
|
||||||
|
|
|
@ -49,7 +49,7 @@ class WalletLabelsTest(BitcoinTestFramework):
|
||||||
assert_equal(response[0]['error']['message'], "Invalid label name")
|
assert_equal(response[0]['error']['message'], "Invalid label name")
|
||||||
|
|
||||||
for rpc_call in rpc_calls:
|
for rpc_call in rpc_calls:
|
||||||
assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, "*")
|
assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, label="*")
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
# Check that there's no UTXO on the node
|
# Check that there's no UTXO on the node
|
||||||
|
|
|
@ -24,34 +24,7 @@ SHA256_SUMS = {
|
||||||
"d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz"},
|
"d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz"},
|
||||||
"1b0a7408c050e3d09a8be8e21e183ef7ee570385dc41216698cc3ab392a484e7": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-osx64.tar.gz"},
|
"1b0a7408c050e3d09a8be8e21e183ef7ee570385dc41216698cc3ab392a484e7": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-osx64.tar.gz"},
|
||||||
"706e0472dbc933ed2757650d54cbcd780fd3829ebf8f609b32780c7eedebdbc9": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-x86_64-linux-gnu.tar.gz"},
|
"706e0472dbc933ed2757650d54cbcd780fd3829ebf8f609b32780c7eedebdbc9": {"tag": "v0.14.3", "tarball": "bitcoin-0.14.3-x86_64-linux-gnu.tar.gz"},
|
||||||
#
|
|
||||||
"d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": {"tag": "v0.15.2", "tarball": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz"},
|
|
||||||
"54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": {"tag": "v0.15.2", "tarball": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz"},
|
|
||||||
"87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": {"tag": "v0.15.2", "tarball": "bitcoin-0.15.2-osx64.tar.gz"},
|
|
||||||
"566be44190fd76daa01f13d428939dadfb8e3daacefc8fa17f433cad28f73bd5": {"tag": "v0.15.2", "tarball": "bitcoin-0.15.2-x86_64-linux-gnu.tar.gz"},
|
|
||||||
#
|
|
||||||
"0768c6c15caffbaca6524824c9563b42c24f70633c681c2744649158aa3fd484": {"tag": "v0.16.3", "tarball": "bitcoin-0.16.3-aarch64-linux-gnu.tar.gz"},
|
|
||||||
"fb2818069854a6ad20ea03b28b55dbd35d8b1f7d453e90b83eace5d0098a2a87": {"tag": "v0.16.3", "tarball": "bitcoin-0.16.3-arm-linux-gnueabihf.tar.gz"},
|
|
||||||
"78c3bff3b619a19aed575961ea43cc9e142959218835cf51aede7f0b764fc25d": {"tag": "v0.16.3", "tarball": "bitcoin-0.16.3-osx64.tar.gz"},
|
|
||||||
"5d422a9d544742bc0df12427383f9c2517433ce7b58cf672b9a9b17c2ef51e4f": {"tag": "v0.16.3", "tarball": "bitcoin-0.16.3-x86_64-linux-gnu.tar.gz"},
|
|
||||||
#
|
|
||||||
"5a6b35d1a348a402f2d2d6ab5aed653a1a1f13bc63aaaf51605e3501b0733b7a": {"tag": "v0.17.2", "tarball": "bitcoin-0.17.2-aarch64-linux-gnu.tar.gz"},
|
|
||||||
"d1913a5d19c8e8da4a67d1bd5205d03c8614dfd2e02bba2fe3087476643a729e": {"tag": "v0.17.2", "tarball": "bitcoin-0.17.2-arm-linux-gnueabihf.tar.gz"},
|
|
||||||
"a783ba20706dbfd5b47fbedf42165fce70fbbc7d78003305d964f6b3da14887f": {"tag": "v0.17.2", "tarball": "bitcoin-0.17.2-osx64.tar.gz"},
|
|
||||||
"943f9362b9f11130177839116f48f809d83478b4c28591d486ee9a7e35179da6": {"tag": "v0.17.2", "tarball": "bitcoin-0.17.2-x86_64-linux-gnu.tar.gz"},
|
|
||||||
#
|
|
||||||
"88f343af72803b851c7da13874cc5525026b0b55e63e1b5e1298390c4688adc6": {"tag": "v0.18.1", "tarball": "bitcoin-0.18.1-aarch64-linux-gnu.tar.gz"},
|
|
||||||
"cc7d483e4b20c5dabd4dcaf304965214cf4934bcc029ca99cbc9af00d3771a1f": {"tag": "v0.18.1", "tarball": "bitcoin-0.18.1-arm-linux-gnueabihf.tar.gz"},
|
|
||||||
"b7bbcee7a7540f711b171d6981f939ca8482005fde22689bc016596d80548bb1": {"tag": "v0.18.1", "tarball": "bitcoin-0.18.1-osx64.tar.gz"},
|
|
||||||
"425ee5ec631ae8da71ebc1c3f5c0269c627cf459379b9b030f047107a28e3ef8": {"tag": "v0.18.1", "tarball": "bitcoin-0.18.1-riscv64-linux-gnu.tar.gz"},
|
|
||||||
"600d1db5e751fa85903e935a01a74f5cc57e1e7473c15fd3e17ed21e202cfe5a": {"tag": "v0.18.1", "tarball": "bitcoin-0.18.1-x86_64-linux-gnu.tar.gz"},
|
|
||||||
#
|
|
||||||
"3a80431717842672df682bdb619e66523b59541483297772a7969413be3502ff": {"tag": "v0.19.1", "tarball": "bitcoin-0.19.1-aarch64-linux-gnu.tar.gz"},
|
|
||||||
"657f28213823d240dd3324d14829702f9ad6f0710f8bdd1c379cb3c447197f48": {"tag": "v0.19.1", "tarball": "bitcoin-0.19.1-arm-linux-gnueabihf.tar.gz"},
|
|
||||||
"1ae1b87de26487075cd2fd22e0d4ead87d969bd55c44f2f1d873ecdc6147ebb3": {"tag": "v0.19.1", "tarball": "bitcoin-0.19.1-osx64.tar.gz"},
|
|
||||||
"aa7a9563b48aa79252c8e7b6a41c07a5441bd9f14c5e4562cc72720ea6cb0ee5": {"tag": "v0.19.1", "tarball": "bitcoin-0.19.1-riscv64-linux-gnu.tar.gz"},
|
|
||||||
"5fcac9416e486d4960e1a946145566350ca670f9aaba99de6542080851122e4c": {"tag": "v0.19.1", "tarball": "bitcoin-0.19.1-x86_64-linux-gnu.tar.gz"},
|
|
||||||
#
|
|
||||||
"60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-aarch64-linux-gnu.tar.gz"},
|
"60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-aarch64-linux-gnu.tar.gz"},
|
||||||
"55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz"},
|
"55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz"},
|
||||||
"b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-osx64.tar.gz"},
|
"b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1": {"tag": "v0.20.1", "tarball": "bitcoin-0.20.1-osx64.tar.gz"},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Copyright 2014 BitPay Inc.
|
# Copyright 2014 BitPay Inc.
|
||||||
# Copyright 2016-2017 The Bitcoin Core developers
|
# Copyright 2016-present The Bitcoin Core developers
|
||||||
# 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.
|
||||||
"""Test framework for bitcoin utils.
|
"""Test framework for bitcoin utils.
|
||||||
|
@ -155,15 +155,16 @@ def bctest(testDir, testObj, buildenv):
|
||||||
|
|
||||||
if "error_txt" in testObj:
|
if "error_txt" in testObj:
|
||||||
want_error = testObj["error_txt"]
|
want_error = testObj["error_txt"]
|
||||||
# Compare error text
|
# A partial match instead of an exact match makes writing tests easier
|
||||||
# TODO: ideally, we'd compare the strings exactly and also assert
|
# and should be sufficient.
|
||||||
# That stderr is empty if no errors are expected. However, bitcoin-tx
|
|
||||||
# emits DISPLAY errors when running as a windows application on
|
|
||||||
# linux through wine. Just assert that the expected error text appears
|
|
||||||
# somewhere in stderr.
|
|
||||||
if want_error not in res.stderr:
|
if want_error not in res.stderr:
|
||||||
logging.error(f"Error mismatch:\nExpected: {want_error}\nReceived: {res.stderr.rstrip()}\nres: {str(res)}")
|
logging.error(f"Error mismatch:\nExpected: {want_error}\nReceived: {res.stderr.rstrip()}\nres: {str(res)}")
|
||||||
raise Exception
|
raise Exception
|
||||||
|
else:
|
||||||
|
if res.stderr:
|
||||||
|
logging.error(f"Unexpected error received: {res.stderr.rstrip()}\nres: {str(res)}")
|
||||||
|
raise Exception
|
||||||
|
|
||||||
|
|
||||||
def parse_output(a, fmt):
|
def parse_output(a, fmt):
|
||||||
"""Parse the output according to specified format.
|
"""Parse the output according to specified format.
|
||||||
|
|
Loading…
Add table
Reference in a new issue