mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 13:42:10 +02:00
c3945bfd2bdoc: use derivehdkey in multisig tutorial (Sjors Provoost)3662e33669test: use derivehdkey in M-of-N multisig demo (Sjors Provoost)d9570f0838rpc: add derivehdkey (Sjors Provoost)62da9f9614wallet: add GetExtKey helper (Sjors Provoost)aaf1548475wallet: generalize GetActiveHDPubKeys helper (Sjors Provoost)3821452c4arefactor: add hardened derivation helper (Sjors Provoost)0ab61caafdrpc: ParsePathBIP32 helper (Sjors Provoost)e36c4b76e1util: reject out-of-range BIP32 keypath indices (Sjors Provoost)ba78c31a00fuzz: check ParseHDKeypath/WriteHDKeypath round-trip (Sjors Provoost)8cce969085Have ParseHDKeypath handle h derivation marker (Sjors Provoost)fc53077762test: move parse_hd_keypath test to bip32_tests (Sjors Provoost)dab525eb77key: add DeriveExtKey() helper (Sjors Provoost) Pull request description: Adds a `derivehdkey` RPC that returns an xpub, or optionally the xprv, at an arbitrary BIP32 path (with at least one hardened step), derived from a wallet HD key. The main use case is coordinating a multisig setup, where each participant shares an xpub derived at a hardened path (e.g. `m/87h/0h/0h`) distinct from their default single-signature descriptors. See the (updated) `doc/multisig-tutorial.md` and (updated) functional test to see how that workflow improves. The first commits are some helpful helpers: - _key: add DeriveExtKey() helper_ - performs the actual derivation - _test: move parse_hd_keypath test to bip32_tests_ - from `psbt_wallet_tests` - _Have ParseHDKeypath handle h derivation marker_ - _util: reject out-of-range BIP32 keypath indices_ - `ParseHDKeypath` would previously map overflowing values without `h` to hardened. - _fuzz: check ParseHDKeypath/WriteHDKeypath round-trip_ - _rpc: ParsePathBIP32 helper_ - _refactor: add hardened derivation helper_ - `HasHardenedDerivation()`, to enforce the "at least one hardened step" rule - _wallet: generalize GetActiveHDPubKeys helper_ - extracts code from `gethdkeys` which `derivehdkey` needs - _wallet: add GetExtKey helper_ - reconstruct an xprv from a wallet xpub (analog of `GetKey()`); behavior-preserving prep, also simplifies `gethdkeys`. Meat and potatoes: - _rpc: add derivehdkey_ - the RPC itself, plus the `UnusedKey` filter on `GetHDPubKeys` that drives key selection. - _test: use derivehdkey in M-of-N multisig demo_ - rewrites the functional multisig test to use the RPC and `<0;1>` syntax. - _doc: use derivehdkey in multisig tutorial_ - same for the prose tutorial. ACKs for top commit: pseudoramdom: code review ACKc3945bfd2bachow101: ACKc3945bfd2bw0xlt: That being the case, ACKc3945bfd2bTree-SHA512: 661f17c9bfe26017eb14c27ba7af37093387100d3baa25f5d29bba9c1aedc40d19afe1bdfc126a18d018857bb02f1fc84386f10b8f4f4b8e9d6f4b0691d9e302
329 lines
15 KiB
C++
329 lines
15 KiB
C++
// Copyright (c) 2013-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <clientversion.h>
|
|
#include <key.h>
|
|
#include <key_io.h>
|
|
#include <streams.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <util/bip32.h>
|
|
#include <util/strencodings.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
struct TestDerivation {
|
|
std::string pub;
|
|
std::string prv;
|
|
unsigned int nChild;
|
|
};
|
|
|
|
struct TestVector {
|
|
std::string strHexMaster;
|
|
std::vector<TestDerivation> vDerive;
|
|
|
|
explicit TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {}
|
|
|
|
TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) {
|
|
vDerive.emplace_back();
|
|
TestDerivation &der = vDerive.back();
|
|
der.pub = pub;
|
|
der.prv = prv;
|
|
der.nChild = nChild;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
TestVector test1 =
|
|
TestVector("000102030405060708090a0b0c0d0e0f")
|
|
("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
|
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
|
0x80000000)
|
|
("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
|
|
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
|
1)
|
|
("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
|
|
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
|
0x80000002)
|
|
("xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
|
|
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
|
2)
|
|
("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
|
|
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
|
1000000000)
|
|
("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
|
|
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
|
0);
|
|
|
|
TestVector test2 =
|
|
TestVector("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542")
|
|
("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
|
|
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
|
0)
|
|
("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
|
|
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
|
0xFFFFFFFF)
|
|
("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
|
|
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
|
1)
|
|
("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
|
|
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
|
0xFFFFFFFE)
|
|
("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
|
|
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
|
2)
|
|
("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
|
|
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
|
0);
|
|
|
|
TestVector test3 =
|
|
TestVector("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be")
|
|
("xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",
|
|
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
|
|
0x80000000)
|
|
("xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",
|
|
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
|
|
0);
|
|
|
|
TestVector test4 =
|
|
TestVector("3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678")
|
|
("xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa",
|
|
"xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv",
|
|
0x80000000)
|
|
("xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m",
|
|
"xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G",
|
|
0x80000001)
|
|
("xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt",
|
|
"xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1",
|
|
0);
|
|
|
|
const std::vector<std::string> TEST5 = {
|
|
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm",
|
|
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH",
|
|
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn",
|
|
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ",
|
|
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4",
|
|
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J",
|
|
"xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv",
|
|
"xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ",
|
|
"xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN",
|
|
"xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8",
|
|
"DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4",
|
|
"DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9",
|
|
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx",
|
|
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G",
|
|
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY",
|
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL"
|
|
};
|
|
|
|
void RunTest(const TestVector& test)
|
|
{
|
|
std::vector<std::byte> seed{ParseHex<std::byte>(test.strHexMaster)};
|
|
CExtKey key;
|
|
CExtPubKey pubkey;
|
|
key.SetSeed(seed);
|
|
pubkey = key.Neuter();
|
|
for (const TestDerivation &derive : test.vDerive) {
|
|
unsigned char data[74];
|
|
key.Encode(data);
|
|
pubkey.Encode(data);
|
|
|
|
// Test private key
|
|
BOOST_CHECK(EncodeExtKey(key) == derive.prv);
|
|
BOOST_CHECK(DecodeExtKey(derive.prv) == key); //ensure a base58 decoded key also matches
|
|
|
|
// Test public key
|
|
BOOST_CHECK(EncodeExtPubKey(pubkey) == derive.pub);
|
|
BOOST_CHECK(DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches
|
|
|
|
// Derive new keys
|
|
CExtKey keyNew;
|
|
BOOST_CHECK(key.Derive(keyNew, derive.nChild));
|
|
CExtPubKey pubkeyNew = keyNew.Neuter();
|
|
if (!(derive.nChild & 0x80000000)) {
|
|
// Compare with public derivation
|
|
CExtPubKey pubkeyNew2;
|
|
BOOST_CHECK(pubkey.Derive(pubkeyNew2, derive.nChild));
|
|
BOOST_CHECK(pubkeyNew == pubkeyNew2);
|
|
}
|
|
key = keyNew;
|
|
pubkey = pubkeyNew;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(bip32_tests, BasicTestingSetup)
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_test1) {
|
|
RunTest(test1);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_test2) {
|
|
RunTest(test2);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_test3) {
|
|
RunTest(test3);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_test4) {
|
|
RunTest(test4);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_test5) {
|
|
for (const auto& str : TEST5) {
|
|
auto dec_extkey = DecodeExtKey(str);
|
|
auto dec_extpubkey = DecodeExtPubKey(str);
|
|
BOOST_CHECK_MESSAGE(!dec_extkey.key.IsValid(), "Decoding '" + str + "' as xprv should fail");
|
|
BOOST_CHECK_MESSAGE(!dec_extpubkey.pubkey.IsValid(), "Decoding '" + str + "' as xpub should fail");
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_derive_ext_key)
|
|
{
|
|
const CExtKey master{DecodeExtKey(test1.vDerive[0].prv)};
|
|
const std::vector<uint32_t> path{test1.vDerive[0].nChild, test1.vDerive[1].nChild};
|
|
const auto derived{DeriveExtKey(master, path)};
|
|
BOOST_REQUIRE(derived);
|
|
BOOST_CHECK(EncodeExtKey(derived->first) == test1.vDerive[2].prv);
|
|
|
|
KeyOriginInfo expected_origin;
|
|
expected_origin.fingerprint = master.id_key_fingerprint();
|
|
expected_origin.path = path;
|
|
BOOST_CHECK(derived->second == expected_origin);
|
|
|
|
const auto root{DeriveExtKey(master, {})};
|
|
BOOST_REQUIRE(root);
|
|
BOOST_CHECK(root->first == master);
|
|
expected_origin.path.clear();
|
|
BOOST_CHECK(root->second == expected_origin);
|
|
|
|
CExtKey max_depth{master};
|
|
for (auto i{0}; i++ < 255;) {
|
|
CExtKey next_key;
|
|
BOOST_REQUIRE(max_depth.Derive(next_key, 0));
|
|
max_depth = next_key;
|
|
}
|
|
BOOST_CHECK(!DeriveExtKey(max_depth, {0}));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_has_hardened_derivation)
|
|
{
|
|
const std::vector<uint32_t> empty;
|
|
const std::vector<uint32_t> unhardened{0, 1, 2};
|
|
const std::vector<uint32_t> hardened{0x80000000U};
|
|
const std::vector<uint32_t> mixed{0, 1 | 0x80000000U, 2};
|
|
BOOST_CHECK(!HasHardenedDerivation(empty));
|
|
BOOST_CHECK(!HasHardenedDerivation(unhardened));
|
|
BOOST_CHECK(HasHardenedDerivation(hardened));
|
|
BOOST_CHECK(HasHardenedDerivation(mixed));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bip32_max_depth) {
|
|
CExtKey key_parent{DecodeExtKey(test1.vDerive[0].prv)}, key_child;
|
|
CExtPubKey pubkey_parent{DecodeExtPubKey(test1.vDerive[0].pub)}, pubkey_child;
|
|
|
|
// We can derive up to the 255th depth..
|
|
for (auto i = 0; i++ < 255;) {
|
|
BOOST_CHECK(key_parent.Derive(key_child, 0));
|
|
std::swap(key_parent, key_child);
|
|
BOOST_CHECK(pubkey_parent.Derive(pubkey_child, 0));
|
|
std::swap(pubkey_parent, pubkey_child);
|
|
}
|
|
|
|
// But trying to derive a non-existent 256th depth will fail!
|
|
BOOST_CHECK(key_parent.nDepth == 255);
|
|
BOOST_CHECK(pubkey_parent.nDepth == 255);
|
|
BOOST_CHECK(!key_parent.Derive(key_child, 0));
|
|
BOOST_CHECK(!pubkey_parent.Derive(pubkey_child, 0));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
|
|
{
|
|
std::vector<uint32_t> keypath;
|
|
|
|
BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("///////////////////////////", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1'/1", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("//////////////////////////'/", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("1///////////////////////////", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1/1'/", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("1/'//////////////////////////", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath(" ", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("0", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("O", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("0000'/0000'/0000'", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("0000,/0000,/0000,", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("01234", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("0x1234", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("1", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath(" 1", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("42", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m42", keypath));
|
|
|
|
// A path element's numeric part is capped at 2^31-1; the top bit is
|
|
// reserved for the hardened marker (h or ').
|
|
BOOST_CHECK(ParseHDKeypath("2147483647", keypath)); // 0x7fffffff, largest normal index
|
|
BOOST_CHECK(!ParseHDKeypath("2147483648", keypath)); // 0x80000000, would set the hardened bit
|
|
BOOST_CHECK(!ParseHDKeypath("4294967295", keypath)); // 0xffffffff
|
|
BOOST_CHECK(!ParseHDKeypath("4294967296", keypath)); // uint32_t max + 1
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("n", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("n/", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("n/0", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0'", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/0''", keypath));
|
|
|
|
keypath.clear();
|
|
BOOST_REQUIRE(ParseHDKeypath("m/0h/1h/2h", keypath));
|
|
BOOST_REQUIRE_EQUAL(keypath.size(), 3);
|
|
BOOST_CHECK_EQUAL(keypath[0], 0x80000000U);
|
|
BOOST_CHECK_EQUAL(keypath[1], 0x80000001U);
|
|
BOOST_CHECK_EQUAL(keypath[2], 0x80000002U);
|
|
BOOST_CHECK(!ParseHDKeypath("m/0hh", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/h0", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0'/0'", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/'0/0'", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0/0", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("n/0/0", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0/0/00", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/0/0/f00", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0/0/000000000000000000000000000000000000000000000000000000000000000000000000000000000000", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/1/1/111111111111111111111111111111111111111111111111111111111111111111111111111111111111", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/0/00/0", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/0'/00/'0", keypath));
|
|
|
|
BOOST_CHECK(ParseHDKeypath("m/1/", keypath));
|
|
BOOST_CHECK(!ParseHDKeypath("m/1//", keypath));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|