Skip to content

Commit 206a3b9

Browse files
committed
Optionally pass BIP388 policy to signtx
1 parent c9d785c commit 206a3b9

File tree

9 files changed

+80
-10
lines changed

9 files changed

+80
-10
lines changed

hwilib/_cli.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,13 @@ def signmessage_handler(args: argparse.Namespace, client: HardwareWalletClient)
9494
return signmessage(client, message=args.message, path=args.path)
9595

9696
def signtx_handler(args: argparse.Namespace, client: HardwareWalletClient) -> Dict[str, Union[bool, str]]:
97-
return signtx(client, psbt=args.psbt)
97+
policy = BIP388Policy(
98+
name=args.policy_name,
99+
descriptor_template=args.policy_desc,
100+
keys_info=args.key,
101+
hmac=args.hmac
102+
)
103+
return signtx(client, psbt=args.psbt, bip388_policy=policy)
98104

99105
def wipe_device_handler(args: argparse.Namespace, client: HardwareWalletClient) -> Dict[str, bool]:
100106
return wipe_device(client)
@@ -167,6 +173,11 @@ def get_parser() -> HWIArgumentParser:
167173

168174
signtx_parser = subparsers.add_parser('signtx', help='Sign a PSBT')
169175
signtx_parser.add_argument('psbt', help='The Partially Signed Bitcoin Transaction to sign')
176+
signtx_policy_group = signtx_parser.add_argument_group("BIP388 policy")
177+
signtx_policy_group.add_argument('--policy-name', help='Registered policy name')
178+
signtx_policy_group.add_argument('--policy-desc', help='Registered policy descriptor template')
179+
signtx_policy_group.add_argument('--key', help='Registered policy key information', action='append')
180+
signtx_policy_group.add_argument('--hmac', help='Registered policy hmac, obtained via register command')
170181
signtx_parser.set_defaults(func=signtx_handler)
171182

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

hwilib/commands.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,11 @@ def getmasterxpub(client: HardwareWalletClient, addrtype: AddressType = AddressT
184184
"""
185185
return {"xpub": client.get_master_xpub(addrtype, account).to_string()}
186186

187-
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]]:
188192
"""
189193
Sign a Partially Signed Bitcoin Transaction (PSBT) with the client.
190194
@@ -196,7 +200,7 @@ def signtx(client: HardwareWalletClient, psbt: str) -> Dict[str, Union[bool, str
196200
# Deserialize the transaction
197201
tx = PSBT()
198202
tx.deserialize(psbt)
199-
result = client.sign_tx(tx).serialize()
203+
result = client.sign_tx(tx, bip388_policy).serialize()
200204
return {"psbt": result, "signed": result != psbt}
201205

202206
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: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,11 @@ def get_pubkey_at_path(self, path: str) -> ExtendedKey:
186186
return ExtendedKey.deserialize(xpub_str)
187187

188188
@ledger_exception
189-
def sign_tx(self, psbt: PSBT) -> PSBT:
189+
def sign_tx(
190+
self,
191+
psbt: PSBT,
192+
bip388_policy: Optional[BIP388Policy]
193+
) -> PSBT:
190194
"""
191195
Sign a transaction with a Ledger device. Not all transactions can be signed by a Ledger.
192196
@@ -199,6 +203,8 @@ def sign_tx(self, psbt: PSBT) -> PSBT:
199203
For application versions 2.1.x and above:
200204
201205
- 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
202208
"""
203209
master_fp = self.get_master_fingerprint()
204210

@@ -265,6 +271,25 @@ def legacy_sign_tx() -> PSBT:
265271
else:
266272
continue
267273

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+
268293
# Check if P2WSH
269294
if is_p2wsh(scriptcode):
270295
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ def get_pubkey_at_path(self, bip32_path: str) -> ExtendedKey:
8282
raise NotImplementedError("The HardwareWalletClient base class "
8383
"does not implement this method")
8484

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

0 commit comments

Comments
 (0)