Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More sp tests #5

Open
wants to merge 6 commits into
base: implement-bip352-full
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/outputtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
static const std::string OUTPUT_TYPE_STRING_BECH32M = "bech32m";
static const std::string OUTPUT_TYPE_STRING_SILENT_PAYMENT = "silent-payment";
static const std::string OUTPUT_TYPE_STRING_SILENT_PAYMENT = "silent-payments";
static const std::string OUTPUT_TYPE_STRING_UNKNOWN = "unknown";

std::optional<OutputType> ParseOutputType(const std::string& type)
Expand Down
4 changes: 4 additions & 0 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

#include <memory>
#include <stdint.h>
#include <variant>

using node::BlockAssembler;
using node::CBlockTemplate;
Expand Down Expand Up @@ -285,6 +286,9 @@ static RPCHelpMan generatetoaddress()
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
if (std::get_if<V0SilentPaymentDestination>(&destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Cannot pay to Silent Payment Output in Coinbase transactions");
}

NodeContext& node = EnsureAnyNodeContext(request.context);
Mining& miner = EnsureMining(node);
Expand Down
5 changes: 4 additions & 1 deletion src/rpc/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,10 @@ class DescribeAddressVisitor

UniValue operator()(const V0SilentPaymentDestination& dest) const
{
return UniValue(UniValue::VOBJ);
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
obj.pushKV("iswitness", false);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to refresh my memory on what iswitness means, but I think this should be true. silent payments v0 addresses will only generate taproot outputs, which are witness outputs.

Copy link
Author

@Eunovo Eunovo Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DescribeAddressVisitor is used in getaddressinfo. I think the question to be answered here is "Do we want getaddressinfo on a V0SilentPaymentAddress to identify it as a witness scriptPubKey?" Note that I say "witness scriptPubKey" not "witness address" because what even is a "witness address"?

return obj;
}

UniValue operator()(const PubKeyDestination& dest) const
Expand Down
26 changes: 25 additions & 1 deletion src/wallet/rpc/addresses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
#include <core_io.h>
#include <key_io.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/solver.h>
#include <silentpaymentkey.h>
#include <util/bip32.h>
#include <util/translation.h>
#include <wallet/receive.h>
#include <wallet/rpc/util.h>
#include <wallet/wallet.h>

#include <univalue.h>
#include <variant>

namespace wallet {
RPCHelpMan getnewaddress()
Expand All @@ -26,7 +29,7 @@ RPCHelpMan getnewaddress()
"so payments received with the address will be associated with 'label'.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", \"bech32m\" and \"silent-payment\"."},
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", \"bech32m\" and \"silent-payments\"."},
},
RPCResult{
RPCResult::Type::STR, "address", "The new bitcoin address"
Expand Down Expand Up @@ -592,6 +595,27 @@ RPCHelpMan getaddressinfo()

UniValue ret(UniValue::VOBJ);

auto sp_dest = std::get_if<V0SilentPaymentDestination>(&dest);
if (sp_dest) {
for (const auto& sp_spk_man : pwallet->GetSilentPaymentsSPKMs()) {
LOCK(sp_spk_man->cs_desc_man);
auto wallet_desc = sp_spk_man->GetWalletDescriptor();
auto sppub = GetSpPubKeyFrom(wallet_desc.descriptor);
CHECK_NONFATAL(sppub.has_value());
auto scan_pubkey = sppub->scanKey.GetPubKey();
if (scan_pubkey != sp_dest->m_scan_pubkey) continue;

SpPubKey dest_sppub(sppub->scanKey, sp_dest->m_spend_pubkey);
std::string sppub_str = EncodeSpPubKey(dest_sppub);
std::string desc_str = "sp("+ sppub_str +")";
FlatSigningProvider keys;
std::string error;
std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, error, false);
ret.pushKV("desc", desc->ToString());
ret.pushKV("parent_desc", wallet_desc.descriptor->ToString());
}
}

std::string currentAddress = EncodeDestination(dest);
ret.pushKV("address", currentAddress);

Expand Down
2 changes: 1 addition & 1 deletion src/wallet/rpc/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ static RPCHelpMan createwalletdescriptor()
"The address type must be one that the wallet does not already have a descriptor for."
+ HELP_REQUIRING_PASSPHRASE,
{
{"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
{"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", \"bech32m\" and \"silent-payments\"."},
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
{"internal", RPCArg::Type::BOOL, RPCArg::DefaultHint{"Both external and internal will be generated unless this parameter is specified"}, "Whether to only make one descriptor that is internal (if parameter is true) or external (if parameter is false)"},
{"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"The HD key used by all other active descriptors"}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for this descriptor's key"},
Expand Down
1 change: 1 addition & 0 deletions src/wallet/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ bool IsInputForSharedSecretDerivation(const CScript& input, const CWallet& walle
case TxoutType::MULTISIG:
case TxoutType::PUBKEY:
case TxoutType::NONSTANDARD:
case TxoutType::ANCHOR:
case TxoutType::NULL_DATA: { return false; }
case TxoutType::WITNESS_UNKNOWN:
// This should never happen, as this step takes place after coin selection
Expand Down
9 changes: 6 additions & 3 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3607,12 +3607,15 @@ std::set<ScriptPubKeyMan*> CWallet::GetAllScriptPubKeyMans() const

ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool internal) const
{
if (type == OutputType::SILENT_PAYMENT && internal) {
return nullptr;
}
const std::map<OutputType, ScriptPubKeyMan*>& spk_managers = internal ? m_internal_spk_managers : m_external_spk_managers;
std::map<OutputType, ScriptPubKeyMan*>::const_iterator it = spk_managers.find(type);
if (it == spk_managers.end()) {
return nullptr;
if (it != spk_managers.end()) {
return it->second;
}
return it->second;
return nullptr;
}

std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const
Expand Down
11 changes: 9 additions & 2 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ def init_wallet(self, *, node):
if wallet_name is not False:
n = self.nodes[node]
if wallet_name is not None:
n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors, load_on_startup=True)
n.createwallet(wallet_name=wallet_name, descriptors=self.options.descriptors, silent_payment=self.options.silent_payments, load_on_startup=True)
n.importprivkey(privkey=n.get_deterministic_priv_key().key, label='coinbase', rescan=True)

# Only enables wallet support when the module is available
Expand All @@ -454,7 +454,7 @@ def run_test(self):

# Public helper methods. These can be accessed by the subclass test scripts.

def add_wallet_options(self, parser, *, descriptors=True, legacy=True):
def add_wallet_options(self, parser, *, descriptors=True, legacy=True, silent_payment=False):
kwargs = {}
if descriptors + legacy == 1:
# If only one type can be chosen, set it as default
Expand All @@ -469,6 +469,13 @@ def add_wallet_options(self, parser, *, descriptors=True, legacy=True):
group.add_argument("--legacy-wallet", action='store_const', const=False, **kwargs,
help="Run test using legacy wallets", dest='descriptors')

if silent_payment:
group.add_argument("--silent_payments", action='store_const', const=True, **kwargs,
Eunovo marked this conversation as resolved.
Show resolved Hide resolved
help="Run test using a silent payment wallet", dest='silent_payments')
else:
group.add_argument("--silent_payments", action='store_const', const=False, **kwargs,
help="Run test using a silent payment wallet", dest='silent_payments')

def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
"""Instantiate TestNode objects.

Expand Down
18 changes: 17 additions & 1 deletion test/functional/wallet_createwalletdescriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class WalletCreateDescriptorTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser, descriptors=True, legacy=False)
self.add_wallet_options(parser, descriptors=True, legacy=False, silent_payment=True)

def set_test_params(self):
self.setup_clean_chain = True
Expand All @@ -38,9 +38,12 @@ def test_basic(self):
xpub = xpub_info[0]["xpub"]
xprv = xpub_info[0]["xprv"]
expected_descs = []
sp_desc = None
for desc in def_wallet.listdescriptors()["descriptors"]:
if desc["desc"].startswith("wpkh("):
expected_descs.append(desc["desc"])
if desc["desc"].startswith("sp("):
sp_desc = desc["desc"]

assert_raises_rpc_error(-5, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'", wallet.createwalletdescriptor, "bech32")
assert_raises_rpc_error(-5, f"Private key for {xpub} is not known", wallet.createwalletdescriptor, type="bech32", hdkey=xpub)
Expand Down Expand Up @@ -77,6 +80,19 @@ def test_basic(self):
assert_equal(new_descs[0][1], True)
assert_equal(new_descs[0][2], True)

old_descs = curr_descs
wallet.createwalletdescriptor(type="silent-payments", internal=False)
curr_descs = set([(d["desc"], d["active"], d["internal"]) for d in wallet.listdescriptors(private=True)["descriptors"]])
new_descs = list(curr_descs - old_descs)
assert_equal(len(new_descs), 1)
assert_equal(len(wallet.gethdkeys()), 1)
assert_equal([d["desc"] for d in wallet.listdescriptors()["descriptors"] if d["desc"].startswith("sp(")][0], sp_desc)
assert_equal(new_descs[0][1], True)
assert_equal(new_descs[0][2], False)

assert_raises_rpc_error(-4, "Descriptor already exists", wallet.createwalletdescriptor, type="silent-payments", internal=False)
assert_raises_rpc_error(-4, "Descriptor already exists", wallet.createwalletdescriptor, type="silent-payments", internal=True) # SP descs are the same for both internal and external

def test_imported_other_keys(self):
self.log.info("Test createwalletdescriptor with multiple keys in active descriptors")
def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
Expand Down
20 changes: 11 additions & 9 deletions test/functional/wallet_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,16 @@ def run_test(self):
assert_raises_rpc_error(-4, 'This wallet has no available keys', nopriv_rpc.getnewaddress)

self.log.info("Test descriptor exports")
self.nodes[0].createwallet(wallet_name='desc_export', descriptors=True)
self.nodes[0].createwallet(wallet_name='desc_export', descriptors=True, silent_payment=True)
exp_rpc = self.nodes[0].get_wallet_rpc('desc_export')
self.nodes[0].createwallet(wallet_name='desc_import', disable_private_keys=True, descriptors=True)
self.nodes[0].createwallet(wallet_name='desc_import', disable_private_keys=True, descriptors=True, silent_payment=True)
imp_rpc = self.nodes[0].get_wallet_rpc('desc_import')

addr_types = [('legacy', False, 'pkh(', '44h/1h/0h', -13),
('p2sh-segwit', False, 'sh(wpkh(', '49h/1h/0h', -14),
('bech32', False, 'wpkh(', '84h/1h/0h', -13),
('bech32m', False, 'tr(', '86h/1h/0h', -13),
('silent-payments', False, 'sp(', '', 0),
('legacy', True, 'pkh(', '44h/1h/0h', -13),
('p2sh-segwit', True, 'sh(wpkh(', '49h/1h/0h', -14),
('bech32', True, 'wpkh(', '84h/1h/0h', -13),
Expand All @@ -233,12 +234,13 @@ def run_test(self):
addr = exp_rpc.getnewaddress(address_type=addr_type)
desc = exp_rpc.getaddressinfo(addr)['parent_desc']
assert_equal(desc_prefix, desc[0:len(desc_prefix)])
idx = desc.index('/') + 1
assert_equal(deriv_path, desc[idx:idx + 9])
if internal:
assert_equal('1', desc[int_idx])
else:
assert_equal('0', desc[int_idx])
if deriv_path:
idx = desc.index('/') + 1
assert_equal(deriv_path, desc[idx:idx + 9])
if internal:
assert_equal('1', desc[int_idx])
else:
assert_equal('0', desc[int_idx])

self.log.info("Testing the same descriptor is returned for address type {} {}".format(addr_type, int_str))
for i in range(0, 10):
Expand All @@ -253,7 +255,7 @@ def run_test(self):
imp_rpc.importdescriptors([{
'desc': desc,
'active': True,
'next_index': 11,
'next_index': 0 if desc_prefix == "sp(" else 11,
'timestamp': 'now',
'internal': internal
}])
Expand Down
4 changes: 2 additions & 2 deletions test/functional/wallet_miniscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def watchonly_test(self, desc):
if desc.startswith("tr("):
addr_type = "bech32m"
elif desc.startswith("sp("):
addr_type = "silent-payment"
addr_type = "silent-payments"

if range is not None:
assert_equal(
Expand Down Expand Up @@ -294,7 +294,7 @@ def signing_test(
self.log.info("Generating an address for it and testing it detects funds")
addr_type = "bech32m"
if is_silent_payments:
addr_type = "silent-payment"
addr_type = "silent-payments"
elif is_taproot == False:
addr_type = "bech32"
addr = self.ms_sig_wallet.getnewaddress(address_type=addr_type)
Expand Down
Loading
Loading