Skip to content

Commit dfd11e0

Browse files
committed
Optionally pass BIP388 policy to signtx
1 parent 42e5adc commit dfd11e0

File tree

9 files changed

+88
-11
lines changed

9 files changed

+88
-11
lines changed

hwilib/_cli.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
)
2323
from .common import (
2424
AddressType,
25+
BIP388Policy,
2526
Chain,
2627
)
2728
from .errors import (
@@ -88,7 +89,13 @@ def signmessage_handler(args: argparse.Namespace, client: HardwareWalletClient)
8889
return signmessage(client, message=args.message, path=args.path)
8990

9091
def signtx_handler(args: argparse.Namespace, client: HardwareWalletClient) -> Dict[str, Union[bool, str]]:
91-
return signtx(client, psbt=args.psbt)
92+
policy = BIP388Policy(
93+
name=args.policy_name,
94+
descriptor_template=args.policy_desc,
95+
keys_info=args.key,
96+
hmac=args.hmac
97+
)
98+
return signtx(client, psbt=args.psbt, bip388_policy=policy)
9299

93100
def wipe_device_handler(args: argparse.Namespace, client: HardwareWalletClient) -> Dict[str, bool]:
94101
return wipe_device(client)
@@ -161,6 +168,11 @@ def get_parser() -> HWIArgumentParser:
161168

162169
signtx_parser = subparsers.add_parser('signtx', help='Sign a PSBT')
163170
signtx_parser.add_argument('psbt', help='The Partially Signed Bitcoin Transaction to sign')
171+
signtx_policy_group = signtx_parser.add_argument_group("BIP388 policy")
172+
signtx_policy_group.add_argument('--policy-name', help='Registered policy name')
173+
signtx_policy_group.add_argument('--policy-desc', help='Registered policy descriptor template')
174+
signtx_policy_group.add_argument('--key', help='Registered policy key information', action='append')
175+
signtx_policy_group.add_argument('--hmac', help='Registered policy hmac, obtained via register command')
164176
signtx_parser.set_defaults(func=signtx_handler)
165177

166178
getxpub_parser = subparsers.add_parser('getxpub', help='Get an extended public key')

hwilib/commands.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from .devices import __all__ as all_devs
5353
from .common import (
5454
AddressType,
55+
BIP388Policy,
5556
Chain,
5657
)
5758
from .hwwclient import HardwareWalletClient
@@ -183,7 +184,11 @@ def getmasterxpub(client: HardwareWalletClient, addrtype: AddressType = AddressT
183184
"""
184185
return {"xpub": client.get_master_xpub(addrtype, account).to_string()}
185186

186-
def signtx(client: HardwareWalletClient, psbt: str) -> Dict[str, Union[bool, str]]:
187+
def signtx(
188+
client: HardwareWalletClient,
189+
psbt: str,
190+
bip388_policy: Optional[BIP388Policy]
191+
) -> Dict[str, Union[bool, str]]:
187192
"""
188193
Sign a Partially Signed Bitcoin Transaction (PSBT) with the client.
189194
@@ -195,7 +200,7 @@ def signtx(client: HardwareWalletClient, psbt: str) -> Dict[str, Union[bool, str
195200
# Deserialize the transaction
196201
tx = PSBT()
197202
tx.deserialize(psbt)
198-
result = client.sign_tx(tx).serialize()
203+
result = client.sign_tx(tx, bip388_policy).serialize()
199204
return {"psbt": result, "signed": result != psbt}
200205

201206
def getxpub(client: HardwareWalletClient, path: str, expert: bool = False) -> Dict[str, Any]:

hwilib/devices/bitbox02.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
)
5858
from ..common import (
5959
AddressType,
60+
BIP388Policy,
6061
Chain,
6162
)
6263

@@ -557,7 +558,11 @@ def display_multisig_address(
557558
return address
558559

559560
@bitbox02_exception
560-
def sign_tx(self, psbt: PSBT) -> PSBT:
561+
def sign_tx(
562+
self,
563+
psbt: PSBT,
564+
__: Optional[BIP388Policy],
565+
) -> PSBT:
561566
"""
562567
Sign a transaction with the BitBox02.
563568

hwilib/devices/coldcard.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
)
5252
from ..common import (
5353
AddressType,
54+
BIP388Policy,
5455
Chain,
5556
)
5657
from functools import wraps
@@ -116,7 +117,11 @@ def get_master_fingerprint(self) -> bytes:
116117
return struct.pack('<I', self.device.master_fingerprint)
117118

118119
@coldcard_exception
119-
def sign_tx(self, psbt: PSBT) -> PSBT:
120+
def sign_tx(
121+
self,
122+
psbt: PSBT,
123+
__: Optional[BIP388Policy],
124+
) -> PSBT:
120125
"""
121126
Sign a transaction with the Coldcard.
122127

hwilib/devices/digitalbitbox.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
from ..common import (
3131
AddressType,
32+
BIP388Policy,
3233
Chain,
3334
hash256,
3435
)
@@ -387,7 +388,11 @@ def get_pubkey_at_path(self, path: str) -> ExtendedKey:
387388
return xpub
388389

389390
@digitalbitbox_exception
390-
def sign_tx(self, psbt: PSBT) -> PSBT:
391+
def sign_tx(
392+
self,
393+
psbt: PSBT,
394+
__: Optional[BIP388Policy],
395+
) -> PSBT:
391396

392397
# Create a transaction with all scriptsigs blanked out
393398
blank_tx = psbt.get_unsigned_tx()

hwilib/devices/jade.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
)
3535
from ..common import (
3636
AddressType,
37+
BIP388Policy,
3738
Chain,
3839
sha256
3940
)
@@ -370,10 +371,15 @@ def _split_at_last_hardened_element(path: Sequence[int]) -> Tuple[Sequence[int],
370371
# Sign tx PSBT - newer Jade firmware supports native PSBT signing, but old firmwares require
371372
# mapping to the legacy 'sign_tx' structures.
372373
@jade_exception
373-
def sign_tx(self, psbt: PSBT) -> PSBT:
374+
def sign_tx(
375+
self,
376+
psbt: PSBT,
377+
__: Optional[BIP388Policy],
378+
) -> PSBT:
374379
"""
375380
Sign a transaction with the Blockstream Jade.
376381
"""
382+
377383
# Old firmware does not have native PSBT handling - use legacy method
378384
if self.PSBT_SUPPORTED_FW_VERSION > self.fw_version.finalize_version():
379385
return self.legacy_sign_tx(psbt)

hwilib/devices/ledger.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from ..common import (
3333
AddressType,
3434
Chain,
35+
BIP388Policy,
3536
)
3637
from .ledger_bitcoin.client import (
3738
createClient,
@@ -185,7 +186,11 @@ def get_pubkey_at_path(self, path: str) -> ExtendedKey:
185186
return ExtendedKey.deserialize(xpub_str)
186187

187188
@ledger_exception
188-
def sign_tx(self, psbt: PSBT) -> PSBT:
189+
def sign_tx(
190+
self,
191+
psbt: PSBT,
192+
bip388_policy: Optional[BIP388Policy]
193+
) -> PSBT:
189194
"""
190195
Sign a transaction with a Ledger device. Not all transactions can be signed by a Ledger.
191196
@@ -198,6 +203,8 @@ def sign_tx(self, psbt: PSBT) -> PSBT:
198203
For application versions 2.1.x and above:
199204
200205
- Only keys derived with standard BIP 44, 49, 84, and 86 derivation paths are supported for single signature addresses.
206+
207+
BIP388: for basic descriptors this is optional, but if provided name must be empty
201208
"""
202209
master_fp = self.get_master_fingerprint()
203210

@@ -264,6 +271,25 @@ def legacy_sign_tx() -> PSBT:
264271
else:
265272
continue
266273

274+
if bip388_policy is not None:
275+
policy = WalletPolicy(
276+
name=bip388_policy.name,
277+
descriptor_template=bip388_policy.descriptor_template,
278+
keys_info=bip388_policy.keys_info
279+
)
280+
if policy.id not in wallets:
281+
if bip388_policy.hmac is None:
282+
raise BadArgumentError("Missing --hmac")
283+
wallets[policy.id] = (
284+
signing_priority[script_addrtype],
285+
script_addrtype,
286+
policy,
287+
bytes.fromhex(bip388_policy.hmac),
288+
)
289+
continue
290+
291+
# No BIP388 policy provided, construct on the fly
292+
267293
# Check if P2WSH
268294
if is_p2wsh(scriptcode):
269295
if len(psbt_in.witness_script) == 0:

hwilib/devices/trezor.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
)
7878
from ..common import (
7979
AddressType,
80+
BIP388Policy,
8081
Chain,
8182
hash256,
8283
)
@@ -355,7 +356,11 @@ def get_pubkey_at_path(self, path: str) -> ExtendedKey:
355356
return xpub
356357

357358
@trezor_exception
358-
def sign_tx(self, psbt: PSBT) -> PSBT:
359+
def sign_tx(
360+
self,
361+
psbt: PSBT,
362+
__: Optional[BIP388Policy],
363+
) -> PSBT:
359364
"""
360365
Sign a transaction with the Trezor. There are some limitations to what transactions can be signed.
361366

hwilib/hwwclient.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
get_bip44_chain,
1818
)
1919
from .psbt import PSBT
20-
from .common import AddressType, Chain
20+
from .common import (
21+
AddressType,
22+
Chain,
23+
BIP388Policy,
24+
)
2125

2226

2327
class HardwareWalletClient(object):
@@ -78,7 +82,11 @@ def get_pubkey_at_path(self, bip32_path: str) -> ExtendedKey:
7882
raise NotImplementedError("The HardwareWalletClient base class "
7983
"does not implement this method")
8084

81-
def sign_tx(self, psbt: PSBT) -> PSBT:
85+
def sign_tx(
86+
self,
87+
psbt: PSBT,
88+
bip388_policy: Optional[BIP388Policy]
89+
) -> PSBT:
8290
"""
8391
Sign a partially signed bitcoin transaction (PSBT).
8492

0 commit comments

Comments
 (0)