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

Add address test for all miniscript fragments #143

Closed
wants to merge 1 commit into from
Closed
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
89 changes: 89 additions & 0 deletions tests/test_get_wallet_address.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from hashlib import sha256
import hmac
import re

from bitcoin_client.ledger_bitcoin import Client, AddressType, MultisigWallet, WalletPolicy
from bitcoin_client.ledger_bitcoin.exception.errors import IncorrectDataError

from .conftest import testnet_to_regtest_addr as T

import pytest

from test_utils import SpeculosGlobals

# TODO: add tests with UI


Expand Down Expand Up @@ -294,3 +301,85 @@ def test_get_wallet_address_large_addr_index(client: Client):
# too large address_index, not allowed for an unhardened step
with pytest.raises(IncorrectDataError):
client.get_wallet_address(wallet, wallet_hmac, 0, 2**31, False)


def test_get_wallet_address_miniscript_all_fragments(client: Client, speculos_globals: SpeculosGlobals, rpc):
# Create some miniscripts to exercise all possible fragments at least once,
# by comparing with the addresses generated by bitcoin-core.
# (currently only covering miniscript in `wsh()`, not on taproot leaves)

# arbitrary 20-bytes and 32-bytes hex strings
H20 = bytes(list(range(20))).hex()
H32 = bytes(list(range(32))).hex()
fragments = [
"or_d(pk(@1/**),0)", # 0 and or_d
"1", # 1
"c:pk_k(@1/**)", # pk_k and c:
"c:pk_h(@1/**)", # pk_h
"older(42)", # older
"after(42)", # after
f"sha256({H32})", # sha256
f"ripemd160({H20})", # ripemd160
f"hash256({H32})", # hash256
f"hash160({H20})", # hash160
"andor(pk(@1/**),older(42),pk(@2/**))", # andor
"and_v(v:pk(@1/**),pk(@2/**))", # and_v and v:
"and_b(pk(@1/**),a:pk(@2/**))", # and_b and a:
"or_b(pk(@1/**),a:pk(@2/**))", # or_b
"t:or_c(pk(@1/**),v:pk(@2/**))", # or_c and t:
# or_d is covered
"or_i(pk(@1/**),pk(@2/**))", # or_i
"thresh(1,pk(@1/**),a:pk(@2/**))", # thresh
"multi(2,@1/**,@2/**,@3/**)", # multi

# WRAPPERS not covered above
# a: is covered
"and_b(1,s:pk(@1/**))", # s:
# c: is covered
"dv:older(42)", # d:
# t: is covered
# v: is covered
"j:pk(@1/**)", # j:
"n:pk(@1/**)", # n:
"l:pk(@1/**)", # l:
"u:pk(@1/**)", # u:
]

def prepend_a(frag):
# prepends the a: wrapper (taking into account that `frag` could already start with wrappers)
if re.match("^[a-z]+:", frag):
return "a" + frag
else:
return "a:" + frag

test_keys = [
"[f5acc2fd/48'/1'/0'/2']tpubDFAqEGNyad35aBCKUAXbQGDjdVhNueno5ZZVEn3sQbW5ci457gLR7HyTmHBg93oourBssgUxuWz1jX5uhc1qaqFo9VsybY1J5FuedLfm4dK",
'tpubDDcmHJ6bsQqSRDzXrF1cgyPfXpFTHmqBUcq5cevfszh83XJtjqXZXDYwP3N82bA51dBVhbe3uaaWwAxW2tEsjgZPXmupQpNwdmULXq1WXDU',
'tpubDCXK744twow5CX8HdAvV4Vez413R4xrM3hgD85mA3EpbnwgvtBmhh18eLAGsL5R9E2mwThPTz9fs4x4ZYgCC6GuuKmzSitH9FgWyqaDEKta',
'tpubDCLxCbopTq5qisZzRcf5ZJ8dHR3PXEexc1vDUR61eGDnSVcXjvEwC9CFXqRPzCi9vmrMd6xfJtFrZY8yrPo5886K1AjJACAviLuEXMNfvbS'
]

is_change = False
addr_index = 3

for fr in fragments:
# We use "and_b(pk(@0/**),a:<miniscript_to_be_tested>})" as a generic gadget to compute a valid descriptor
# that can be registered, as long as the <miniscript_to_be_tested> if valid and safe.
# The key placeholders in <miniscript_to_be_tested> must start from @1 (if present).

desc_tmpl = f"wsh(and_b(pk(@0/**),{prepend_a(fr)}))"
n_keys = desc_tmpl.count("@")

assert n_keys <= len(test_keys), "add more tpubs to the test_keys"

wallet_policy = WalletPolicy("A policy", desc_tmpl, test_keys[:n_keys])

wallet_hmac = hmac.new(speculos_globals.wallet_registration_key, wallet_policy.id, sha256).digest()
addr_hww = client.get_wallet_address(wallet_policy, wallet_hmac, is_change, addr_index, False)

desc = wallet_policy.get_descriptor(is_change)
# compute descriptor checksum and derive the address
desc_chk = rpc.getdescriptorinfo(desc)["descriptor"]
addr_core = rpc.deriveaddresses(desc_chk, [3, 3])[0]

assert T(addr_hww) == addr_core
Loading