import ustruct from trezor import wire from trezor.crypto.curve import ed25519 from trezor.crypto.hashlib import sha256 from trezor.messages import LiskTransactionType from trezor.messages.LiskSignedTx import LiskSignedTx from trezor.utils import HashWriter from . import layout from .helpers import LISK_CURVE, get_address_from_public_key from apps.common import seed async def lisk_sign_tx(ctx, msg): pubkey, seckey = await _get_keys(ctx, msg) transaction = _update_raw_tx(msg.transaction, pubkey) try: await _require_confirm_by_type(ctx, transaction) except AttributeError: raise wire.DataError('The transaction has invalid asset data field') await layout.require_confirm_fee(ctx, transaction.amount, transaction.fee) txbytes = _get_transaction_bytes(transaction) txhash = HashWriter(sha256) for field in txbytes: txhash.extend(field) digest = txhash.get_digest() signature = ed25519.sign(seckey, digest) return LiskSignedTx(signature=signature) async def _get_keys(ctx, msg): address_n = msg.address_n or () node = await seed.derive_node(ctx, address_n, LISK_CURVE) seckey = node.private_key() pubkey = node.public_key() pubkey = pubkey[1:] # skip ed25519 pubkey marker return pubkey, seckey def _update_raw_tx(transaction, pubkey): transaction.sender_public_key = pubkey # For CastVotes transactions, recipientId should be equal to transaction # creator address. if transaction.type == LiskTransactionType.CastVotes: transaction.recipient_id = get_address_from_public_key(pubkey) return transaction async def _require_confirm_by_type(ctx, transaction): if transaction.type == LiskTransactionType.Transfer: return await layout.require_confirm_tx( ctx, transaction.recipient_id, transaction.amount) if transaction.type == LiskTransactionType.RegisterDelegate: return await layout.require_confirm_delegate_registration( ctx, transaction.asset.delegate.username) if transaction.type == LiskTransactionType.CastVotes: return await layout.require_confirm_vote_tx( ctx, transaction.asset.votes) if transaction.type == LiskTransactionType.RegisterSecondPassphrase: return await layout.require_confirm_public_key( ctx, transaction.asset.signature.public_key) if transaction.type == LiskTransactionType.RegisterMultisignatureAccount: return await layout.require_confirm_multisig( ctx, transaction.asset.multisignature) raise wire.DataError('Invalid transaction type') def _get_transaction_bytes(tx): # Required transaction parameters t_type = ustruct.pack('Q', 0) else: # Lisk uses big-endian for recipient_id, string -> int -> bytes t_recipient_id = ustruct.pack('>Q', int(tx.recipient_id[:-1])) t_amount = ustruct.pack('