Skip to content

Commit 511ff28

Browse files
committed
tests: add sending functional tests
1 parent a62cca2 commit 511ff28

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
'wallet_listtransactions.py',
140140
'wallet_miniscript.py',
141141
# vv Tests less than 30s vv
142+
'wallet_silentpayments_sending.py',
142143
'p2p_invalid_messages.py',
143144
'rpc_createmultisig.py',
144145
'p2p_timeouts.py --v1transport',
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env python3
2+
from test_framework.address import address_to_scriptpubkey
3+
from test_framework.blocktools import COINBASE_MATURITY
4+
from test_framework.descriptors import descsum_create
5+
from test_framework.messages import COIN
6+
from test_framework.test_framework import BitcoinTestFramework
7+
from test_framework.util import assert_raises_rpc_error
8+
from test_framework.wallet import MiniWallet
9+
10+
SILENT_PAYMENT_ADDRESS = "sprt1qqtzwfsu4f34wejks0nxwzed3zq6vh53cg2rnxj9w6ncyrmy95dxx7qnvd47fskn470t9tl4z8a8nul5k3fztquqp4fjrarl7d5lphu7rk52s4hsp"
11+
12+
13+
class SilentPaymentsSendingTest(BitcoinTestFramework):
14+
def set_test_params(self):
15+
self.num_nodes = 3
16+
self.extra_args = [[], ["-txindex=1"], []]
17+
18+
def skip_test_if_missing_module(self):
19+
self.skip_if_no_wallet()
20+
21+
def init_wallet(self, *, node):
22+
pass
23+
24+
def watch_only_wallet_send(self):
25+
self.log.info(
26+
"Testing if a watch only wallet is not able to send silent transaction"
27+
)
28+
self.nodes[0].createwallet(
29+
wallet_name="watch_only_wallet",
30+
descriptors=True,
31+
disable_private_keys=True,
32+
blank=True,
33+
)
34+
watch_only_wallet = self.nodes[0].get_wallet_rpc("watch_only_wallet")
35+
desc_import = [
36+
{
37+
"desc": descsum_create(
38+
"wpkh(tpubD6NzVbkrYhZ4YNXVQbNhMK1WqguFsUXceaVJKbmno2aZ3B6QfbMeraaYvnBSGpV3vxLyTTK9DYT1yoEck4XUScMzXoQ2U2oSmE2JyMedq3H/0/*)"
39+
),
40+
"timestamp": "now",
41+
"internal": False,
42+
"active": True,
43+
"keypool": True,
44+
"range": [0, 100],
45+
"watchonly": True,
46+
}
47+
]
48+
watch_only_wallet.importdescriptors(desc_import)
49+
self.generatetoaddress(
50+
self.nodes[0], COINBASE_MATURITY + 10, watch_only_wallet.getnewaddress()
51+
)
52+
self.log.info(
53+
"Watch-only wallets cannot send coins using silent_payment option"
54+
)
55+
assert_raises_rpc_error(
56+
-4,
57+
"Silent payments require access to private keys to build transactions.",
58+
watch_only_wallet.sendtoaddress,
59+
SILENT_PAYMENT_ADDRESS,
60+
21,
61+
)
62+
63+
def encrypted_wallet_send(self):
64+
self.log.info("Testing if encrypted SP wallet")
65+
self.nodes[0].createwallet(
66+
wallet_name="encrypted_wallet", descriptors=True, passphrase="passphrase"
67+
)
68+
encrypted_wallet = self.nodes[0].get_wallet_rpc("encrypted_wallet")
69+
self.generatetoaddress(
70+
self.nodes[0], COINBASE_MATURITY + 10, encrypted_wallet.getnewaddress()
71+
)
72+
self.log.info("encrypted wallets must be able to send coins after decryption")
73+
outputs = [
74+
{"bcrt1pk0yzk76w2p55ykyjyfeq99td069c257se9nwugl7cl5geadq944spyc330": 15}
75+
]
76+
77+
# send RPC can be run without decrypting the wallet and it must generate a incomplete PSBT
78+
tx = encrypted_wallet.send(outputs=outputs, options={"add_to_wallet": False})
79+
assert not tx["complete"]
80+
81+
# but when silent_payment option is enabled, wallet must be decrypted
82+
assert_raises_rpc_error(
83+
-13,
84+
"Please enter the wallet passphrase with walletpassphrase first.",
85+
encrypted_wallet.sendtoaddress,
86+
SILENT_PAYMENT_ADDRESS,
87+
21,
88+
)
89+
90+
encrypted_wallet.walletpassphrase("passphrase", 20)
91+
txid = encrypted_wallet.sendtoaddress(
92+
address=SILENT_PAYMENT_ADDRESS,
93+
amount=21,
94+
)
95+
assert txid
96+
97+
def test_simple_send(self):
98+
self.log.info("Testing a simple send")
99+
self.nodes[0].createwallet(wallet_name="simple_send")
100+
simple_send = self.nodes[0].get_wallet_rpc("simple_send")
101+
self.generatetoaddress(
102+
self.nodes[0], COINBASE_MATURITY + 10, simple_send.getnewaddress()
103+
)
104+
txid = simple_send.sendtoaddress(
105+
address=SILENT_PAYMENT_ADDRESS,
106+
amount=21,
107+
)
108+
assert txid
109+
110+
def test_deterministic_send(self):
111+
# set up and fund the funder wallet. we will use this wallet to create UTXOs
112+
# at specific addresses in the sending wallet
113+
# using miniwallet for determinism
114+
funder = MiniWallet(self.nodes[0])
115+
116+
# set up the sender wallet
117+
xprv1 = "tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52"
118+
descriptor = {
119+
"desc": descsum_create("wpkh(" + xprv1 + "/0h/0h/*h)"),
120+
"timestamp": "now",
121+
"active": True,
122+
}
123+
self.nodes[1].createwallet("sending_wallet", descriptors=True)
124+
sender = self.nodes[1].get_wallet_rpc("sending_wallet")
125+
sender.importdescriptors([descriptor])
126+
127+
# fund the sender wallet with two utxos and mine a block
128+
address_one = sender.getnewaddress()
129+
address_two = sender.getnewaddress()
130+
funder.send_to(
131+
from_node=self.nodes[0],
132+
scriptPubKey=address_to_scriptpubkey(address_one),
133+
amount=10 * COIN,
134+
)
135+
funder.send_to(
136+
from_node=self.nodes[0],
137+
scriptPubKey=address_to_scriptpubkey(address_two),
138+
amount=10 * COIN,
139+
)
140+
self.generate(self.nodes[0], 1)
141+
142+
# recipient address: generated with wallet seed hex `f00dbabe`
143+
silent_payment_address = "sprt1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xcrdz399"
144+
amount = 15.0
145+
txid = sender.sendtoaddress(address=silent_payment_address, amount=amount)
146+
tx = sender.getrawtransaction(txid, True)
147+
for output in tx["vout"]:
148+
if output["value"] == amount:
149+
assert (
150+
output["scriptPubKey"]["address"]
151+
# == "bcrt1pfjcwk0este8fn8c77p78djswa2hram3m2v4nwg2jjzdkmv8299ys5sv99h" -- this is what it used to be
152+
# TODO: figure out why this changed. it shouldnt have but need to verify that the old value was in fact
153+
# the correct one
154+
== "bcrt1p3ayfzayc4zqxdkeyxfzae5qhd9aqgxrw8dz4s2pd33xtvvtfvc4q5r42c4"
155+
)
156+
break
157+
else:
158+
assert False
159+
160+
def test_address_reuse(self):
161+
self.nodes[0].createwallet(wallet_name="miner_wallet", descriptors=True)
162+
miner_wallet = self.nodes[0].get_wallet_rpc("miner_wallet")
163+
self.generatetoaddress(
164+
self.nodes[0], COINBASE_MATURITY + 10, miner_wallet.getnewaddress()
165+
)
166+
167+
self.nodes[0].createwallet(wallet_name="sender_wallet", descriptors=True)
168+
sender_wallet = self.nodes[0].get_wallet_rpc("sender_wallet")
169+
sender_address = sender_wallet.getnewaddress()
170+
171+
miner_wallet.send(outputs=[{sender_address: 5}])
172+
miner_wallet.send(outputs=[{sender_address: 7}])
173+
174+
self.generate(self.nodes[0], 8, sync_fun=self.no_op)
175+
176+
silent_payment_address = "sprt1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xcrdz399"
177+
txid1 = sender_wallet.sendtoaddress(address=silent_payment_address, amount=4)
178+
txid2 = sender_wallet.sendtoaddress(address=silent_payment_address, amount=6)
179+
180+
output1 = ""
181+
output2 = ""
182+
for output in sender_wallet.getrawtransaction(txid1, True)["vout"]:
183+
if output["value"] == 4.0:
184+
output1 = output["scriptPubKey"]["address"]
185+
186+
for output in sender_wallet.getrawtransaction(txid2, True)["vout"]:
187+
if output["value"] == 6.0:
188+
output2 = output["scriptPubKey"]["address"]
189+
190+
assert output1 != output2
191+
192+
def run_test(self):
193+
self.watch_only_wallet_send()
194+
self.encrypted_wallet_send()
195+
self.test_simple_send()
196+
self.test_deterministic_send()
197+
self.test_address_reuse()
198+
199+
200+
if __name__ == "__main__":
201+
SilentPaymentsSendingTest(__file__).main()

0 commit comments

Comments
 (0)