You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/src/apps/lisk/sign_tx.py

151 lines
4.6 KiB

import ustruct
6 years ago
from trezor import wire
from trezor.crypto.curve import ed25519
from trezor.crypto.hashlib import sha256
from trezor.enums import LiskTransactionType
from trezor.messages import LiskSignedTx
from trezor.utils import HashWriter
from apps.common import paths
from apps.common.keychain import auto_keychain
from . import layout
from .helpers import get_address_from_public_key
@auto_keychain(__name__)
async def sign_tx(ctx, msg, keychain):
await paths.validate_path(ctx, keychain, msg.address_n)
pubkey, seckey = _get_keys(keychain, msg)
transaction = _update_raw_tx(msg.transaction, pubkey)
try:
await _require_confirm_by_type(ctx, transaction)
except AttributeError:
6 years ago
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)
def _get_keys(keychain, msg):
node = keychain.derive(msg.address_n)
seckey = node.private_key()
pubkey = node.public_key()
pubkey = pubkey[1:] # skip ed25519 pubkey marker
return pubkey, seckey
def _update_raw_tx(transaction, pubkey):
# If device is using for second signature sender_public_key must be exist in transaction
if not transaction.sender_public_key:
transaction.sender_public_key = pubkey
# For CastVotes transactions, recipientId should be equal to transaction
# creator address.
if transaction.type == LiskTransactionType.CastVotes:
if not transaction.recipient_id:
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(
6 years ago
ctx, transaction.recipient_id, transaction.amount
)
if transaction.type == LiskTransactionType.RegisterDelegate:
return await layout.require_confirm_delegate_registration(
6 years ago
ctx, transaction.asset.delegate.username
)
if transaction.type == LiskTransactionType.CastVotes:
6 years ago
return await layout.require_confirm_vote_tx(ctx, transaction.asset.votes)
if transaction.type == LiskTransactionType.RegisterSecondPassphrase:
return await layout.require_confirm_public_key(
6 years ago
ctx, transaction.asset.signature.public_key
)
if transaction.type == LiskTransactionType.RegisterMultisignatureAccount:
return await layout.require_confirm_multisig(
6 years ago
ctx, transaction.asset.multisignature
)
6 years ago
raise wire.DataError("Invalid transaction type")
def _get_transaction_bytes(tx):
# Required transaction parameters
6 years ago
t_type = ustruct.pack("<b", tx.type)
t_timestamp = ustruct.pack("<i", tx.timestamp)
t_sender_public_key = tx.sender_public_key
6 years ago
t_requester_public_key = tx.requester_public_key or b""
if not tx.recipient_id:
# Value can be empty string
6 years ago
t_recipient_id = ustruct.pack(">Q", 0)
else:
# Lisk uses big-endian for recipient_id, string -> int -> bytes
6 years ago
t_recipient_id = ustruct.pack(">Q", int(tx.recipient_id[:-1]))
6 years ago
t_amount = ustruct.pack("<Q", tx.amount)
t_asset = _get_asset_data_bytes(tx)
6 years ago
t_signature = tx.signature or b""
6 years ago
return (
t_type,
t_timestamp,
t_sender_public_key,
t_requester_public_key,
t_recipient_id,
t_amount,
t_asset,
t_signature,
)
def _get_asset_data_bytes(msg):
if msg.type == LiskTransactionType.Transfer:
# Transfer transaction have optional data field
if msg.asset.data is not None:
return msg.asset.data.encode()
else:
6 years ago
return b""
if msg.type == LiskTransactionType.RegisterDelegate:
return msg.asset.delegate.username.encode()
if msg.type == LiskTransactionType.CastVotes:
return ("".join(msg.asset.votes)).encode()
if msg.type == LiskTransactionType.RegisterSecondPassphrase:
return msg.asset.signature.public_key
if msg.type == LiskTransactionType.RegisterMultisignatureAccount:
6 years ago
data = b""
data += ustruct.pack("<b", msg.asset.multisignature.min)
data += ustruct.pack("<b", msg.asset.multisignature.life_time)
data += ("".join(msg.asset.multisignature.keys_group)).encode()
return data
6 years ago
raise wire.DataError("Invalid transaction type")