mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-26 18:02:35 +00:00
python/trezorctl: support multisig addresses in get-address
This commit is contained in:
parent
cea634158a
commit
95f33a77c7
@ -12,6 +12,7 @@ _At the moment, the project does **not** adhere to [Semantic Versioning](https:/
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `trezorctl set unsafe-prompts` controls the new "unsafe prompts" feature. [#1126]
|
- `trezorctl set unsafe-prompts` controls the new "unsafe prompts" feature. [#1126]
|
||||||
|
- `trezorctl btc get-address` can create multisig addresses.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import base64
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
import construct as c
|
||||||
|
|
||||||
from .. import btc, messages, protobuf, tools
|
from .. import btc, messages, protobuf, tools
|
||||||
from . import ChoiceType, with_client
|
from . import ChoiceType, with_client
|
||||||
@ -37,6 +38,34 @@ OUTPUT_SCRIPTS = {
|
|||||||
DEFAULT_COIN = "Bitcoin"
|
DEFAULT_COIN = "Bitcoin"
|
||||||
|
|
||||||
|
|
||||||
|
XpubStruct = c.Struct(
|
||||||
|
"version" / c.Int32ub,
|
||||||
|
"depth" / c.Int8ub,
|
||||||
|
"fingerprint" / c.Int32ub,
|
||||||
|
"child_num" / c.Int32ub,
|
||||||
|
"chain_code" / c.Bytes(32),
|
||||||
|
"key" / c.Bytes(33),
|
||||||
|
c.Terminated,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def xpub_deserialize(xpubstr):
|
||||||
|
xpub_bytes = tools.b58check_decode(xpubstr)
|
||||||
|
data = XpubStruct.parse(xpub_bytes)
|
||||||
|
node = messages.HDNodeType(
|
||||||
|
depth=data.depth,
|
||||||
|
fingerprint=data.fingerprint,
|
||||||
|
child_num=data.child_num,
|
||||||
|
chain_code=data.chain_code,
|
||||||
|
)
|
||||||
|
if data.key[0] == 0:
|
||||||
|
node.private_key = data.key[1:]
|
||||||
|
else:
|
||||||
|
node.public_key = data.key
|
||||||
|
|
||||||
|
return data.version, node
|
||||||
|
|
||||||
|
|
||||||
@click.group(name="btc")
|
@click.group(name="btc")
|
||||||
def cli():
|
def cli():
|
||||||
"""Bitcoin and Bitcoin-like coins commands."""
|
"""Bitcoin and Bitcoin-like coins commands."""
|
||||||
@ -52,13 +81,66 @@ def cli():
|
|||||||
@click.option("-n", "--address", required=True, help="BIP-32 path")
|
@click.option("-n", "--address", required=True, help="BIP-32 path")
|
||||||
@click.option("-t", "--script-type", type=ChoiceType(INPUT_SCRIPTS), default="address")
|
@click.option("-t", "--script-type", type=ChoiceType(INPUT_SCRIPTS), default="address")
|
||||||
@click.option("-d", "--show-display", is_flag=True)
|
@click.option("-d", "--show-display", is_flag=True)
|
||||||
|
@click.option("-x", "--multisig-xpub", multiple=True, help="XPUBs of multisig owners")
|
||||||
|
@click.option("-m", "--multisig-threshold", type=int, help="Number of signatures")
|
||||||
|
@click.option(
|
||||||
|
"-N",
|
||||||
|
"--multisig-suffix-length",
|
||||||
|
help="BIP-32 suffix length for multisig",
|
||||||
|
type=int,
|
||||||
|
default=2,
|
||||||
|
)
|
||||||
@with_client
|
@with_client
|
||||||
def get_address(client, coin, address, script_type, show_display):
|
def get_address(
|
||||||
"""Get address for specified path."""
|
client,
|
||||||
|
coin,
|
||||||
|
address,
|
||||||
|
script_type,
|
||||||
|
show_display,
|
||||||
|
multisig_xpub,
|
||||||
|
multisig_threshold,
|
||||||
|
multisig_suffix_length,
|
||||||
|
):
|
||||||
|
"""Get address for specified path.
|
||||||
|
|
||||||
|
To obtain a multisig address, provide XPUBs of all signers (including your own) in
|
||||||
|
the intended order. All XPUBs should be on the same level. By default, it is assumed
|
||||||
|
that the XPUBs are on the account level, and the last two components of --address
|
||||||
|
should be derived from all of them.
|
||||||
|
|
||||||
|
For BIP-45 multisig:
|
||||||
|
|
||||||
|
$ trezorctl btc get-public-node -n m/45h/0
|
||||||
|
xpub0101
|
||||||
|
$ trezorctl btc get-address -n m/45h/0/0/7 -m 3 -x xpub0101 -x xpub0202 -x xpub0303
|
||||||
|
|
||||||
|
This assumes that the other signers also created xpubs at address "m/45h/i".
|
||||||
|
For all the signers, the final keys will be derived with the "/0/7" suffix.
|
||||||
|
|
||||||
|
You can specify a different suffix length by using the -N option. For example, to
|
||||||
|
use final xpubs, specify '-N 0'.
|
||||||
|
"""
|
||||||
coin = coin or DEFAULT_COIN
|
coin = coin or DEFAULT_COIN
|
||||||
address_n = tools.parse_path(address)
|
address_n = tools.parse_path(address)
|
||||||
|
|
||||||
|
if multisig_xpub:
|
||||||
|
if multisig_threshold is None:
|
||||||
|
raise click.ClickException("Please specify signature threshold")
|
||||||
|
|
||||||
|
multisig_suffix = address_n[-multisig_suffix_length:]
|
||||||
|
nodes = [xpub_deserialize(x)[1] for x in multisig_xpub]
|
||||||
|
multisig = messages.MultisigRedeemScriptType(
|
||||||
|
nodes=nodes, address_n=multisig_suffix, m=multisig_threshold
|
||||||
|
)
|
||||||
|
if script_type == messages.InputScriptType.SPENDADDRESS:
|
||||||
|
script_type = messages.InputScriptType.SPENDMULTISIG
|
||||||
return btc.get_address(
|
return btc.get_address(
|
||||||
client, coin, address_n, show_display, script_type=script_type
|
client,
|
||||||
|
coin,
|
||||||
|
address_n,
|
||||||
|
show_display,
|
||||||
|
script_type=script_type,
|
||||||
|
multisig=multisig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user