apps: introduce Keychain API

pull/25/head
Jan Pochyla 6 years ago
parent 312c252bc1
commit 5bc47fc567

@ -1,8 +1,27 @@
from trezor.crypto import base58, crc, hashlib
from . import cbor
from apps.cardano import cbor
from apps.common import HARDENED
from apps.common.seed import remove_ed25519_prefix
from apps.common import HARDENED, seed
def derive_address_and_node(keychain, path: list):
node = keychain.derive(path)
address_payload = None
address_attributes = {}
address_root = _get_address_root(node, address_payload)
address_type = 0
address_data = [address_root, address_attributes, address_type]
address_data_encoded = cbor.encode(address_data)
address = base58.encode(
cbor.encode(
[cbor.Tagged(24, address_data_encoded), crc.crc32(address_data_encoded)]
)
)
return (address, node)
def validate_full_path(path: list) -> bool:
@ -36,31 +55,9 @@ def _address_hash(data) -> bytes:
def _get_address_root(node, payload):
extpubkey = seed.remove_ed25519_prefix(node.public_key()) + node.chain_code()
extpubkey = remove_ed25519_prefix(node.public_key()) + node.chain_code()
if payload:
payload = {1: cbor.encode(payload)}
else:
payload = {}
return _address_hash([0, [0, extpubkey], payload])
def derive_address_and_node(root_node, path: list):
derived_node = root_node.clone()
address_payload = None
address_attributes = {}
for indice in path:
derived_node.derive_cardano(indice)
address_root = _get_address_root(derived_node, address_payload)
address_type = 0
address_data = [address_root, address_attributes, address_type]
address_data_encoded = cbor.encode(address_data)
address = base58.encode(
cbor.encode(
[cbor.Tagged(24, address_data_encoded), crc.crc32(address_data_encoded)]
)
)
return (address, derived_node)

@ -1,28 +1,23 @@
from trezor import log, ui, wire
from trezor.crypto import bip32
from trezor.messages.CardanoAddress import CardanoAddress
from .address import derive_address_and_node, validate_full_path
from .layout import confirm_with_pagination
from apps.common import paths, seed, storage
from apps.cardano import seed
from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination
from apps.common import paths
async def get_address(ctx, msg):
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
keychain = await seed.get_keychain(ctx)
mnemonic = storage.get_mnemonic()
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
try:
address, _ = derive_address_and_node(root_node, msg.address_n)
address, _ = derive_address_and_node(keychain, msg.address_n)
except ValueError as e:
if __debug__:
log.exception(__name__, e)
raise wire.ProcessError("Deriving address failed")
mnemonic = None
root_node = None
if msg.show_display:
if not await confirm_with_pagination(

@ -1,42 +1,38 @@
from ubinascii import hexlify
from trezor import log, wire
from trezor.crypto import bip32
from trezor.messages.CardanoPublicKey import CardanoPublicKey
from trezor.messages.HDNodeType import HDNodeType
from .address import derive_address_and_node
from apps.common import layout, paths, seed, storage
from apps.cardano import seed
from apps.cardano.address import derive_address_and_node
from apps.common import layout, paths
from apps.common.seed import remove_ed25519_prefix
async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(
ctx, paths.validate_path_for_get_public_key, path=msg.address_n, slip44_id=1815
)
mnemonic = storage.get_mnemonic()
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
try:
key = _get_public_key(root_node, msg.address_n)
key = _get_public_key(keychain, msg.address_n)
except ValueError as e:
if __debug__:
log.exception(__name__, e)
raise wire.ProcessError("Deriving public key failed")
mnemonic = None
root_node = None
if msg.show_display:
await layout.show_pubkey(ctx, key.node.public_key)
return key
def _get_public_key(root_node, derivation_path: list):
_, node = derive_address_and_node(root_node, derivation_path)
def _get_public_key(keychain, derivation_path: list):
_, node = derive_address_and_node(keychain, derivation_path)
public_key = hexlify(seed.remove_ed25519_prefix(node.public_key())).decode()
public_key = hexlify(remove_ed25519_prefix(node.public_key())).decode()
chain_code = hexlify(node.chain_code()).decode()
xpub_key = public_key + chain_code
@ -45,7 +41,7 @@ def _get_public_key(root_node, derivation_path: list):
child_num=node.child_num(),
fingerprint=node.fingerprint(),
chain_code=node.chain_code(),
public_key=seed.remove_ed25519_prefix(node.public_key()),
public_key=remove_ed25519_prefix(node.public_key()),
)
return CardanoPublicKey(node=node_type, xpub=xpub_key)

@ -0,0 +1,41 @@
from trezor import wire
from trezor.crypto import bip32
from apps.common import HARDENED, cache, storage
from apps.common.request_passphrase import protect_by_passphrase
class Keychain:
def __init__(self, root: bip32.HDNode):
self.root = root
def derive(self, path: list) -> bip32.HDNode:
self.validate_path(path)
node = self.root.clone()
for i in path:
node.derive_cardano(i)
return node
def validate_path(self, path: list) -> None:
if len(path) < 2 or len(path) > 5:
raise wire.ProcessError("Derivation path must be composed from 2-5 indices")
if path[0] != HARDENED | 44 or path[1] != HARDENED | 1815:
raise wire.ProcessError("This is not cardano derivation path")
async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized():
# device does not have any seed
raise wire.ProcessError("Device is not initialized")
# acquire passphrase
passphrase = cache.get_passphrase()
if passphrase is None:
passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase)
# compute the seed from mnemonic and passphrase
root = bip32.from_mnemonic_cardano(storage.get_mnemonic(), passphrase)
keychain = Keychain(root)
return keychain

@ -1,18 +1,17 @@
from trezor import log, ui, wire
from trezor.crypto import base58, bip32, hashlib
from trezor.crypto import base58, hashlib
from trezor.crypto.curve import ed25519
from trezor.messages.CardanoSignedTx import CardanoSignedTx
from trezor.messages.CardanoTxRequest import CardanoTxRequest
from trezor.messages.MessageType import CardanoTxAck
from trezor.ui.text import BR
from .address import derive_address_and_node, validate_full_path
from .layout import confirm_with_pagination, progress
from apps.cardano import cbor
from apps.common import seed, storage
from apps.cardano import cbor, seed
from apps.cardano.address import derive_address_and_node, validate_full_path
from apps.cardano.layout import confirm_with_pagination, progress
from apps.common.layout import address_n_to_str, split_address
from apps.common.paths import validate_path
from apps.common.seed import remove_ed25519_prefix
from apps.homescreen.homescreen import display_homescreen
@ -80,9 +79,7 @@ async def request_transaction(ctx, tx_req: CardanoTxRequest, index: int):
async def sign_tx(ctx, msg):
mnemonic = storage.get_mnemonic()
passphrase = await seed._get_cached_passphrase(ctx)
root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
keychain = await seed.get_keychain(ctx)
progress.init(msg.transactions_count, "Loading data")
@ -103,7 +100,7 @@ async def sign_tx(ctx, msg):
# sign the transaction bundle and prepare the result
transaction = Transaction(
msg.inputs, msg.outputs, transactions, root_node, msg.network
msg.inputs, msg.outputs, transactions, keychain, msg.network
)
tx_body, tx_hash = transaction.serialise_tx()
tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash)
@ -135,12 +132,12 @@ def _micro_ada_to_ada(amount: float) -> float:
class Transaction:
def __init__(
self, inputs: list, outputs: list, transactions: list, root_node, network: int
self, inputs: list, outputs: list, transactions: list, keychain, network: int
):
self.inputs = inputs
self.outputs = outputs
self.transactions = transactions
self.root_node = root_node
self.keychain = keychain
# attributes have to be always empty in current Cardano
self.attributes = {}
if network == 1:
@ -170,7 +167,7 @@ class Transaction:
nodes = []
for input in self.inputs:
_, node = derive_address_and_node(self.root_node, input.address_n)
_, node = derive_address_and_node(self.keychain, input.address_n)
nodes.append(node)
for index, output_index in enumerate(output_indexes):
@ -198,7 +195,7 @@ class Transaction:
for output in self.outputs:
if output.address_n:
address, _ = derive_address_and_node(self.root_node, output.address_n)
address, _ = derive_address_and_node(self.keychain, output.address_n)
change_addresses.append(address)
change_derivation_paths.append(output.address_n)
change_coins.append(output.amount)
@ -225,7 +222,7 @@ class Transaction:
node.private_key(), node.private_key_ext(), message
)
extended_public_key = (
seed.remove_ed25519_prefix(node.public_key()) + node.chain_code()
remove_ed25519_prefix(node.public_key()) + node.chain_code()
)
witnesses.append(
[

@ -7,38 +7,43 @@ from apps.common.request_passphrase import protect_by_passphrase
_DEFAULT_CURVE = "secp256k1"
async def derive_node(
ctx: wire.Context, path: list, curve_name: str = _DEFAULT_CURVE
) -> bip32.HDNode:
seed = await _get_cached_seed(ctx)
node = bip32.from_seed(seed, curve_name)
node.derive_path(path)
return node
class Keychain:
def __init__(self, seed: bytes):
self.seed = seed
def derive(self, path: list, curve_name: str = _DEFAULT_CURVE) -> bip32.HDNode:
node = bip32.from_seed(self.seed, curve_name)
node.derive_path(path)
return node
async def _get_cached_seed(ctx: wire.Context) -> bytes:
async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized():
# device does not have any seed
raise wire.ProcessError("Device is not initialized")
if cache.get_seed() is None:
passphrase = await _get_cached_passphrase(ctx)
seed = cache.get_seed()
if seed is None:
# acquire passphrase
passphrase = cache.get_passphrase()
if passphrase is None:
passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase)
# compute the seed from mnemonic and passphrase
seed = bip39.seed(storage.get_mnemonic(), passphrase)
cache.set_seed(seed)
return cache.get_seed()
async def _get_cached_passphrase(ctx: wire.Context) -> str:
if cache.get_passphrase() is None:
passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase)
return cache.get_passphrase()
keychain = Keychain(seed)
return keychain
def derive_node_without_passphrase(
path: list, curve_name: str = _DEFAULT_CURVE
) -> bip32.HDNode:
if not storage.is_initialized():
# device does not have any seed
raise Exception("Device is not initialized")
seed = bip39.seed(storage.get_mnemonic(), "")
node = bip32.from_seed(seed, curve_name)
node.derive_path(path)

@ -1,20 +1,19 @@
from .address import ethereum_address_hex, validate_full_path
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha3_256
from trezor.messages.EthereumAddress import EthereumAddress
from apps.common import paths
from apps.common import paths, seed
from apps.common.layout import address_n_to_str, show_address, show_qr
from apps.ethereum import networks
from apps.ethereum.address import ethereum_address_hex, validate_full_path
async def get_address(ctx, msg):
from trezor.messages.EthereumAddress import EthereumAddress
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha3_256
from apps.common import seed
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n)
node = keychain.derive(msg.address_n)
seckey = node.private_key()
public_key = secp256k1.publickey(seckey, False) # uncompressed
address = sha3_256(public_key[1:], keccak=True).digest()[12:]

@ -21,11 +21,12 @@ def message_digest(message):
async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
await require_confirm_sign_message(ctx, msg.message)
node = await seed.derive_node(ctx, msg.address_n)
node = keychain.derive(msg.address_n)
signature = secp256k1.sign(
node.private_key(),
message_digest(msg.message),

@ -131,8 +131,9 @@ async def send_request_chunk(ctx, data_left: int):
async def send_signature(ctx, msg: EthereumSignTx, digest):
node = await seed.derive_node(ctx, msg.address_n)
keychain = await seed.get_keychain(ctx)
node = keychain.derive(msg.address_n)
signature = secp256k1.sign(
node.private_key(), digest, False, secp256k1.CANONICAL_SIG_ETHEREUM
)

@ -7,9 +7,11 @@ from apps.common.layout import address_n_to_str, show_address, show_qr
async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
node = keychain.derive(msg.address_n, LISK_CURVE)
pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker
address = get_address_from_public_key(pubkey)

@ -6,9 +6,11 @@ from apps.common import layout, paths, seed
async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
node = keychain.derive(msg.address_n, LISK_CURVE)
pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker

@ -23,10 +23,12 @@ def message_digest(message):
async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
await require_confirm_sign_message(ctx, msg.message)
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
node = keychain.derive(msg.address_n, LISK_CURVE)
seckey = node.private_key()
pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker

@ -14,9 +14,11 @@ from apps.common import paths, seed
async def sign_tx(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, validate_full_path, path=msg.address_n)
pubkey, seckey = await _get_keys(ctx, msg)
pubkey, seckey = _get_keys(keychain, msg)
transaction = _update_raw_tx(msg.transaction, pubkey)
try:
@ -37,8 +39,8 @@ async def sign_tx(ctx, msg):
return LiskSignedTx(signature=signature)
async def _get_keys(ctx, msg):
node = await seed.derive_node(ctx, msg.address_n, LISK_CURVE)
def _get_keys(keychain, msg):
node = keychain.derive(msg.address_n, LISK_CURVE)
seckey = node.private_key()
pubkey = node.public_key()

@ -12,7 +12,9 @@ async def get_creds(ctx, address_n=None, network_type=None):
curve = "ed25519"
else:
curve = "secp256k1"
node = await seed.derive_node(ctx, address_n, curve)
keychain = await seed.get_keychain(ctx)
node = keychain.derive(address_n, curve)
if use_slip0010:
key_seed = node.private_key()

@ -9,10 +9,12 @@ from apps.common.paths import validate_path
async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
network = validate_network(msg.network)
await validate_path(ctx, check_path, path=msg.address_n, network=msg.network)
await validate_path(ctx, check_path, path=msg.address_n, network=network)
node = await seed.derive_node(ctx, msg.address_n, NEM_CURVE)
node = keychain.derive(msg.address_n, NEM_CURVE)
address = node.nem_address(network)
if msg.show_display:

@ -11,12 +11,15 @@ from apps.common.paths import validate_path
async def sign_tx(ctx, msg: NEMSignTx):
keychain = await seed.get_keychain(ctx)
validate(msg)
await validate_path(
ctx, check_path, path=msg.transaction.address_n, network=msg.transaction.network
)
node = await seed.derive_node(ctx, msg.transaction.address_n, NEM_CURVE)
node = keychain.derive(msg.transaction.address_n, NEM_CURVE)
if msg.multisig:
public_key = msg.multisig.signer

@ -8,9 +8,11 @@ from apps.common.layout import address_n_to_str, show_address, show_qr
async def get_address(ctx, msg: RippleGetAddress):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n)
node = keychain.derive(msg.address_n)
pubkey = node.public_key()
address = helpers.address_from_public_key(pubkey)

@ -12,10 +12,13 @@ from apps.common import paths, seed
async def sign_tx(ctx, msg: RippleSignTx):
keychain = await seed.get_keychain(ctx)
validate(msg)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n)
node = keychain.derive(msg.address_n)
source_address = helpers.address_from_public_key(node.public_key())
set_canonical_flag(msg)

@ -7,9 +7,11 @@ from apps.stellar import helpers
async def get_address(ctx, msg: StellarGetAddress):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.STELLAR_CURVE)
node = keychain.derive(msg.address_n, helpers.STELLAR_CURVE)
pubkey = seed.remove_ed25519_prefix(node.public_key())
address = helpers.address_from_public_key(pubkey)

@ -13,14 +13,16 @@ from apps.stellar.operations import process_operation
async def sign_tx(ctx, msg: StellarSignTx):
if msg.num_operations == 0:
raise ProcessError("Stellar: At least one operation is required")
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, consts.STELLAR_CURVE)
node = keychain.derive(msg.address_n, consts.STELLAR_CURVE)
pubkey = seed.remove_ed25519_prefix(node.public_key())
if msg.num_operations == 0:
raise ProcessError("Stellar: At least one operation is required")
w = bytearray()
await _init(ctx, w, pubkey, msg)
_timebounds(w, msg.timebounds_start, msg.timebounds_end)

@ -7,8 +7,11 @@ from apps.tezos import helpers
async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
pk = seed.remove_ed25519_prefix(node.public_key())
pkh = hashlib.blake2b(pk, outlen=20).digest()

@ -10,9 +10,11 @@ from apps.tezos import helpers
async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
pk = seed.remove_ed25519_prefix(node.public_key())
pk_prefixed = helpers.base58_encode_check(pk, prefix=helpers.TEZOS_PUBLICKEY_PREFIX)

@ -10,8 +10,11 @@ from apps.tezos import helpers, layout
async def sign_tx(ctx, msg):
keychain = await seed.get_keychain(ctx)
await paths.validate_path(ctx, helpers.validate_full_path, path=msg.address_n)
node = await seed.derive_node(ctx, msg.address_n, helpers.TEZOS_CURVE)
node = keychain.derive(msg.address_n, helpers.TEZOS_CURVE)
if msg.transaction is not None:
to = _get_address_from_contract(msg.transaction.destination)

@ -9,6 +9,8 @@ from apps.common.confirm import require_confirm
async def cipher_key_value(ctx, msg):
keychain = await seed.get_keychain(ctx)
if len(msg.value) % 16 > 0:
raise wire.DataError("Value length must be a multiple of 16")
@ -23,7 +25,7 @@ async def cipher_key_value(ctx, msg):
text.normal(msg.key)
await require_confirm(ctx, text)
node = await seed.derive_node(ctx, msg.address_n)
node = keychain.derive(msg.address_n)
value = compute_cipher_key_value(msg, node.private_key())
return CipheredKeyValue(value=value)

@ -8,6 +8,8 @@ from apps.wallet.sign_tx import addresses
async def get_address(ctx, msg):
keychain = await seed.get_keychain(ctx)
coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name)
@ -19,7 +21,7 @@ async def get_address(ctx, msg):
script_type=msg.script_type,
)
node = await seed.derive_node(ctx, msg.address_n, curve_name=coin.curve_name)
node = keychain.derive(msg.address_n, coin.curve_name)
address = addresses.get_address(msg.script_type, coin, node, msg.multisig)
address_short = addresses.address_short(coin, address)

@ -14,6 +14,8 @@ from apps.wallet.sign_identity import (
async def get_ecdh_session_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
if msg.ecdsa_curve_name is None:
msg.ecdsa_curve_name = "secp256k1"
@ -22,7 +24,7 @@ async def get_ecdh_session_key(ctx, msg):
await require_confirm_ecdh_session_key(ctx, msg.identity)
address_n = get_ecdh_path(identity, msg.identity.index or 0)
node = await seed.derive_node(ctx, address_n, msg.ecdsa_curve_name)
node = keychain.derive(address_n, msg.ecdsa_curve_name)
session_key = ecdh(
seckey=node.private_key(),

@ -7,14 +7,14 @@ from apps.common import coins, layout, seed
async def get_public_key(ctx, msg):
keychain = await seed.get_keychain(ctx)
coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name)
curve_name = msg.ecdsa_curve_name or coin.curve_name
script_type = msg.script_type or InputScriptType.SPENDADDRESS
curve_name = msg.ecdsa_curve_name
if not curve_name:
curve_name = coin.curve_name
node = await seed.derive_node(ctx, msg.address_n, curve_name=curve_name)
node = keychain.derive(msg.address_n, curve_name=curve_name)
if (
script_type in [InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG]

@ -11,6 +11,8 @@ from apps.common.confirm import require_confirm
async def sign_identity(ctx, msg):
keychain = await seed.get_keychain(ctx)
if msg.ecdsa_curve_name is None:
msg.ecdsa_curve_name = "secp256k1"
@ -19,7 +21,7 @@ async def sign_identity(ctx, msg):
await require_confirm_sign_identity(ctx, msg.identity, msg.challenge_visual)
address_n = get_identity_path(identity, msg.identity.index or 0)
node = await seed.derive_node(ctx, address_n, msg.ecdsa_curve_name)
node = keychain.derive(address_n, msg.ecdsa_curve_name)
coin = coins.by_name("Bitcoin")
if msg.ecdsa_curve_name == "secp256k1":

@ -12,6 +12,8 @@ from apps.wallet.sign_tx.addresses import get_address, validate_full_path
async def sign_message(ctx, msg):
keychain = await seed.get_keychain(ctx)
message = msg.message
address_n = msg.address_n
coin_name = msg.coin_name or "Bitcoin"
@ -19,7 +21,6 @@ async def sign_message(ctx, msg):
coin = coins.by_name(coin_name)
await require_confirm_sign_message(ctx, message)
await validate_path(
ctx,
validate_full_path,
@ -29,7 +30,7 @@ async def sign_message(ctx, msg):
validate_script_type=False,
)
node = await seed.derive_node(ctx, address_n, curve_name=coin.curve_name)
node = keychain.derive(address_n, coin.curve_name)
seckey = node.private_key()
address = get_address(script_type, coin, node)

@ -19,7 +19,8 @@ async def sign_tx(ctx, msg):
coin_name = msg.coin_name or "Bitcoin"
coin = coins.by_name(coin_name)
# TODO: rework this so we don't have to pass root to signing.sign_tx
root = await seed.derive_node(ctx, [], curve_name=coin.curve_name)
keychain = await seed.get_keychain(ctx)
root = keychain.derive([], coin.curve_name)
signer = signing.sign_tx(msg, root)
res = None

Loading…
Cancel
Save