Skip to content

Commit

Permalink
Allow ext keys (#3)
Browse files Browse the repository at this point in the history
* rpc/deriveaddress: Add support for sp desc

* spscriptpubkeyman: Move labelled destination generation to seperate methods

* rpc/getnewaddress: Add silent-payment to addess_type list

* descriptor: Allow extended keys in sp()

* Fix scan key storage issues in watch only wallet

- Stop adding scan key to singingprovider private key map, this will prevent scan key from being counted among private keys
- Stop storing scan keys to db, scan keys are already saved in descriptor

* spscriptpubkeyman: Fix MarkUnusedAddresses SegFault

DescriptorScriptPubKeyMan::MarkUnusedAddresses tries to expand the descriptor and read the resulting scripts
This does not work for sp descriptors as no scripts are returned
This leads to a SegFault

* test: Add E2E test for sp descriptor
  • Loading branch information
Eunovo authored and josibake committed Aug 7, 2024
1 parent dcd3744 commit 73b948e
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 89 deletions.
15 changes: 15 additions & 0 deletions src/addresstype.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <attributes.h>
#include <pubkey.h>
#include <script/script.h>
#include <silentpaymentkey.h>
#include <uint256.h>
#include <util/check.h>
#include <util/hash_type.h>
Expand Down Expand Up @@ -129,6 +130,20 @@ struct V0SilentPaymentDestination
CPubKey m_scan_pubkey;
CPubKey m_spend_pubkey;

V0SilentPaymentDestination() = default;

V0SilentPaymentDestination(const CPubKey& scan_pubkey, const CPubKey& spend_pubkey) : m_scan_pubkey(scan_pubkey), m_spend_pubkey(spend_pubkey) {};

V0SilentPaymentDestination(const SpPubKey& sppubkey) {
m_scan_pubkey = sppubkey.scanKey.GetPubKey();
m_spend_pubkey = sppubkey.spendKey;
}

V0SilentPaymentDestination(const SpKey& spkey) {
m_scan_pubkey = spkey.scanKey.GetPubKey();
m_spend_pubkey = spkey.spendKey.GetPubKey();
}

friend bool operator==(const V0SilentPaymentDestination& a, const V0SilentPaymentDestination& b) {
if (a.m_scan_pubkey != b.m_scan_pubkey) return false;
if (a.m_spend_pubkey != b.m_spend_pubkey) return false;
Expand Down
4 changes: 4 additions & 0 deletions src/rpc/output_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ static RPCHelpMan deriveaddresses()

addresses.push_back(EncodeDestination(dest));
}

for (const auto& sppubkey_pair: provider.sppubkeys) {
addresses.push_back(EncodeDestination(V0SilentPaymentDestination(sppubkey_pair.second)));
}
}

// This should not be possible, but an assert seems overkill:
Expand Down
129 changes: 95 additions & 34 deletions src/script/descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,10 @@ class SilentPubkeyProvider final : public PubkeyProvider
std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
pubkey = m_sppk.scanKey.GetPubKey();
}
void SetSpendPubKey(const CPubKey pubkey)
{
m_sppk.spendKey = pubkey;
}
};

/** Base class for all Descriptor implementations. */
Expand Down Expand Up @@ -830,11 +834,20 @@ class DescriptorImpl : public Descriptor
for (const auto& p : m_pubkey_args) {
try {
auto sppubkeyprovider = dynamic_cast<SilentPubkeyProvider&>(*p);
SpKey key;
sppubkeyprovider.GetSpKey(provider, key);
out.spkeys.emplace(key.Neuter().GetID(), key);
out.keys.emplace(key.spendKey.GetPubKey().GetID(), key.spendKey);
out.keys.emplace(key.scanKey.GetPubKey().GetID(), key.scanKey);
{
SpKey key;
if (sppubkeyprovider.GetSpKey(provider, key)) {
out.spkeys.emplace(key.Neuter().GetID(), key);
out.keys.emplace(key.spendKey.GetPubKey().GetID(), key.spendKey);
out.keys.emplace(key.scanKey.GetPubKey().GetID(), key.scanKey);
}
}
{
SpPubKey key;
if (sppubkeyprovider.GetSpPubKey(key)) {
out.keys.emplace(key.scanKey.GetPubKey().GetID(), key.scanKey);
}
}
continue;
} catch (const std::bad_cast&) {}

Expand Down Expand Up @@ -1428,7 +1441,9 @@ enum class ParseScriptContext {
P2WPKH, //!< Inside wpkh() (no script, pubkey only)
P2WSH, //!< Inside wsh() (script becomes v0 witness script)
P2TR, //!< Inside tr() (either internal key, or BIP342 script leaf)
SP, //!< Inside sp() (spkeys are only valid under sp())
SP_ONLY, //!< Argument inside sp(<spkey>) variant (spkeys are only valid under sp())
SP_SCAN, //!< First argument inside sp()
SP_SPEND, //!< Second argument inside sp()
};

/**
Expand Down Expand Up @@ -1484,6 +1499,10 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
error = "Hybrid public keys are not allowed";
return nullptr;
}
if (pubkey.IsValid() && ctx == ParseScriptContext::SP_SCAN) {
error = "Scan key must be a private key or extended private key";
return nullptr;
}
if (pubkey.IsFullyValid()) {
if (permit_uncompressed || pubkey.IsCompressed()) {
return std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false);
Expand Down Expand Up @@ -1520,24 +1539,17 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
CExtKey extkey = DecodeExtKey(str);
CExtPubKey extpubkey = DecodeExtPubKey(str);

if (ctx == ParseScriptContext::SP) {
if (extkey.key.IsValid() || extpubkey.pubkey.IsValid()) {
error = "extended keys are not allowed";
return nullptr;
}

if (ctx == ParseScriptContext::SP_ONLY) {
if (spkey.IsValid()) {
out.keys.emplace(spkey.scanKey.GetPubKey().GetID(), spkey.scanKey);
out.keys.emplace(spkey.spendKey.GetPubKey().GetID(), spkey.spendKey);
return std::make_unique<SilentPubkeyProvider>(key_exp_index, spkey.Neuter());
}

if (sppubkey.IsValid()) {
out.keys.emplace(sppubkey.scanKey.GetPubKey().GetID(), sppubkey.scanKey);
return std::make_unique<SilentPubkeyProvider>(key_exp_index, sppubkey);
}

error = "provided key is not a valid silent payment key";
error = strprintf("key '%s' is not a valid sp key", str);
return nullptr;
}

Expand All @@ -1556,10 +1568,57 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(uint32_t key_exp_index, const S
type = DeriveType::HARDENED;
}
if (!ParseKeyPath(split, path, apostrophe, error)) return nullptr;

if (ctx == ParseScriptContext::SP_SCAN || ctx == ParseScriptContext::SP_SPEND) {
CExtKey derivedKey;
CExtPubKey derivedPubKey;
for (const auto& p : path) {
derivedKey = extkey;
derivedPubKey = extpubkey;

if (derivedKey.key.IsValid()) {
if (!derivedKey.Derive(derivedKey, p)) {
error = "Failed to derive key";
return nullptr;
}
}
if (derivedPubKey.pubkey.IsValid()) {
if (!derivedPubKey.Derive(derivedPubKey, p)) {
error = "Failed to derive key";
return nullptr;
}
}
}

if (ctx == ParseScriptContext::SP_SCAN) {
if (!extkey.key.IsValid()) {
error = "Scan key must be a private key or extended private key";
return nullptr;
}

// Derive sp scan key from extkey
CKey scan_key = derivedKey.key.IsValid() ? derivedKey.key : extkey.key;
return std::make_unique<SilentPubkeyProvider>(key_exp_index, SpPubKey(scan_key));
}

if (extkey.key.IsValid()) {
// Derive sp spend key from extkey
CKey spend_key = derivedKey.key.IsValid() ? derivedKey.key : extkey.key;
out.keys.emplace(spend_key.GetPubKey().GetID(), spend_key);
return std::make_unique<ConstPubkeyProvider>(key_exp_index, spend_key.GetPubKey(), false);
}
if (extpubkey.pubkey.IsValid()) {
// Derive sp spend key from extpubkey
CPubKey spend_key = derivedPubKey.pubkey.IsValid() ? derivedPubKey.pubkey : extpubkey.pubkey;
return std::make_unique<ConstPubkeyProvider>(key_exp_index, spend_key, false);
}
}

if (extkey.key.IsValid()) {
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
}

return std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move(path), type, apostrophe);
}

Expand Down Expand Up @@ -1744,39 +1803,41 @@ std::unique_ptr<DescriptorImpl> ParseScript(uint32_t& key_exp_index, Span<const

if (ctx == ParseScriptContext::TOP && Func("sp", expr)) {
auto arg1 = Expr(expr);
auto firstKey = ParsePubkey(key_exp_index, arg1, ParseScriptContext::SP, out, error);
if (!firstKey) {
if (!Const(",", expr)) {
auto sppKey = ParsePubkey(key_exp_index, arg1, ParseScriptContext::SP_ONLY, out, error);
if (!sppKey) {
error = strprintf("sp(): %s", error);
return nullptr;
}
++key_exp_index;
return std::make_unique<SpDescriptor>(std::move(sppKey));
}
auto arg2 = Expr(expr);

auto scanKey = ParsePubkey(key_exp_index, arg1, ParseScriptContext::SP_SCAN, out, error);
if (!scanKey) {
error = strprintf("sp(): %s", error);
return nullptr;
}
++key_exp_index;
if (!Const(",", expr)) {
return std::make_unique<SpDescriptor>(std::move(firstKey));
}
auto arg2 = Expr(expr);
auto spendKey = ParsePubkey(key_exp_index, arg2, ParseScriptContext::SP, out, error);

auto spendKey = ParsePubkey(key_exp_index, arg2, ParseScriptContext::SP_SPEND, out, error);
if (!spendKey) {
error = strprintf("sp(): %s", error);
return nullptr;
}
auto scanPubKey = firstKey->GetRootPubKey();
auto spendPubKey = spendKey->GetRootPubKey();
if (!scanPubKey.has_value()) {
error = "sp(): could not get scan pubkey";
return nullptr;
}
if (!spendPubKey.has_value()) {
error = "sp(): could not get spend pubkey";
return nullptr;
}
auto it = out.keys.find(scanPubKey->GetID());
if (it == out.keys.end()) {
error = "sp(): requires the scan priv key";
return nullptr;
}
auto sppk = std::make_unique<SilentPubkeyProvider>(key_exp_index, SpPubKey(it->second, *spendPubKey));

SilentPubkeyProvider* sppubKey = dynamic_cast<SilentPubkeyProvider*>(scanKey.get());
assert(sppubKey != nullptr);
sppubKey->SetSpendPubKey(*spendPubKey);

++key_exp_index;
return std::make_unique<SpDescriptor>(std::move(sppk));
return std::make_unique<SpDescriptor>(std::move(scanKey));
} else if (Func("sp", expr)) {
error = "Can only have sp() at top level";
return nullptr;
Expand Down
12 changes: 12 additions & 0 deletions src/silentpaymentkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ struct SpPubKey {

SpPubKey() = default;

/**
* Creates an incomplete SpPubKey.
*/
SpPubKey(CKey scan_key) : scanKey(scan_key)
{
memset(version, 0, sizeof(version));
memset(vchFingerprint, 0, sizeof(vchFingerprint));
maximumNumberOfLabels = 0;
CPubKey dummySpendPubKey;
spendKey = dummySpendPubKey;
}

SpPubKey(CKey scan_key, CPubKey spend_key) : scanKey(scan_key), spendKey(spend_key)
{
memset(version, 0, sizeof(version));
Expand Down
68 changes: 68 additions & 0 deletions src/test/descriptor_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,27 @@ void CheckInferDescriptor(const std::string& script_hex, const std::string& expe
BOOST_CHECK_EQUAL(desc->ToString(), expected_desc + "#" + checksum);
}

void CheckSilentPayments(const std::string& desc, const std::string& expected_private_string, const std::string& expected_public_string, const std::string& expected_norm_string, int flags = 0)
{
FlatSigningProvider keys;
std::string error;
auto parsed_desc = Parse(desc, keys, error, false);
BOOST_CHECK_MESSAGE(parsed_desc, error);

if (~flags & MISSING_PRIVKEYS) {
std::string private_string;
BOOST_CHECK(parsed_desc->ToPrivateString(keys, private_string));
BOOST_CHECK_MESSAGE(EqualDescriptor(private_string, expected_private_string), "Private: " + private_string + " Expected: " + expected_private_string);
}

std::string public_string = parsed_desc->ToString();
std::string norm_string;
BOOST_CHECK(parsed_desc->ToNormalizedString(keys, norm_string));

BOOST_CHECK_MESSAGE(EqualDescriptor(public_string, expected_public_string), "Public: " + public_string + " Expected: " + expected_public_string);
BOOST_CHECK_MESSAGE(EqualDescriptor(norm_string, expected_norm_string), "Normalized: " + norm_string + " Expected: " + expected_norm_string);
}

}

BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
Expand Down Expand Up @@ -657,6 +678,53 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
CheckInferDescriptor("76a914a31725c74421fadc50d35520ab8751ed120af80588ac", "pkh(04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31)", {}, {{"04c56fe4a92d401bcbf1b3dfbe4ac3dac5602ca155a3681497f02c1b9a733b92d704e2da6ec4162e4846af9236ef4171069ac8b7f8234a8405b6cadd96f34f5a31", ""}});
// Infer pk() from p2pk with uncompressed key
CheckInferDescriptor("4104032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220ac", "pk(04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220)", {}, {{"04032540df1d3c7070a8ab3a9cdd304dfc7fd1e6541369c53c4c3310b2537d91059afc8b8e7673eb812a32978dabb78c40f2e423f7757dca61d11838c7aeeb5220", ""}});

// Silent Payments
// Check that /* uses default derivation path for SP
CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/*)",
"sp(spprv1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5qw3uewwg77eaq9rth6er3vj0yutvs5xyup0ndsrg2ffwgheppkkdg3tdld9)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)");

// Check that no path uses default derivation path for SP
CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(spprv1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5qw3uewwg77eaq9rth6er3vj0yutvs5xyup0ndsrg2ffwgheppkkdg3tdld9)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)");

// Check that provided path is used instead of the default derivation path for SP
CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/0h)",
"sp(spprv1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5qwmvhpf70wwlfxmkfmfm8dargka4qgec2fkmxcpvr3tgkezxs2l6sf4t658)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5p457zxv2j2yzn9ha42hxhf3fkqdz5pc5hykqevp765qrrsdn7vc4skd7hrr)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5p457zxv2j2yzn9ha42hxhf3fkqdz5pc5hykqevp765qrrsdn7vc4skd7hrr)");

CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/0h,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(spprv1qqqqqqqqqqqqqq8dkts5l8h805ndmya5ank735tw6syvu9ymdnvqkpc45tv3rg90agqw3uewwg77eaq9rth6er3vj0yutvs5xyup0ndsrg2ffwgheppkkdgnemptj)",
"sp(sppub1qqqqqqqqqqqqqq8dkts5l8h805ndmya5ank735tw6syvu9ymdnvqkpc45tv3rg90agpnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtss5pst7p)",
"sp(sppub1qqqqqqqqqqqqqq8dkts5l8h805ndmya5ank735tw6syvu9ymdnvqkpc45tv3rg90agpnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtss5pst7p)");

// Check that xpubs are accepted for spend key
CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5pnngmqzvcpt976aaqlhevn5qkv2y7sk42j0mpd7yzsut507jwgtssknx4ck)",
MISSING_PRIVKEYS);

CheckSilentPayments("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/0)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5p8cjcfl7ucts5c4ln7tqfjvm9ledmcpdyq4s55kz6rmssl903az0qr6947z)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5p8cjcfl7ucts5c4ln7tqfjvm9ledmcpdyq4s55kz6rmssl903az0qr6947z)",
"sp(sppub1qqqqqqqqqqqqqq8g7vh8y00v7sz34mav3ckf8jw9kg2rzwqhekcp59y5hytussmtx5p8cjcfl7ucts5c4ln7tqfjvm9ledmcpdyq4s55kz6rmssl903az0qr6947z)",
MISSING_PRIVKEYS);

CheckUnparsable("sp(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)",
"sp(): key 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8' is not a valid sp key");
CheckUnparsable("sp(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(): Scan key must be a private key or extended private key");
CheckUnparsable("sp(spprv1qqqqqqqqqqqqqqqcw78khf9nvvgng9a0vsnzgz9hc29vqta5gwhwls59y60grpjpnvqryr9c920g3tru2cs3nazwqjdapvhx249ph95zkp3scsg957vzqag8ml0eq,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi)",
"sp(sppub1qqqqqqqqqqqqqqqcw78khf9nvvgng9a0vsnzgz9hc29vqta5gwhwls59y60grpjpnvper07qpyg2fg4r32cfll4wytqcf9km7zzjaqd0aw69y2tdw0g2y2cklv80y,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)",
"sp(): key 'sppub1qqqqqqqqqqqqqqqcw78khf9nvvgng9a0vsnzgz9hc29vqta5gwhwls59y60grpjpnvper07qpyg2fg4r32cfll4wytqcf9km7zzjaqd0aw69y2tdw0g2y2cklv80y' is not valid");
}

BOOST_AUTO_TEST_SUITE_END()
Loading

0 comments on commit 73b948e

Please sign in to comment.