1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-16 03:18:09 +00:00

stellar: transaction operations

This commit is contained in:
Tomas Susanka 2018-05-24 15:33:03 +02:00
parent 00fb252063
commit 17a4ccd268
4 changed files with 254 additions and 19 deletions

View File

@ -0,0 +1,40 @@
from trezor.messages import wire_types
from micropython import const
# source: https://github.com/stellar/go/blob/master/xdr/Stellar-transaction.x
op_codes = {
'StellarAccountMergeOp': const(8),
'StellarAllowTrustOp': const(7),
'StellarBumpSequenceOp': const(11),
'StellarChangeTrustOp': const(6),
'StellarCreateAccountOp': const(0),
'StellarCreatePassiveOfferOp': const(4),
'StellarManageDataOp': const(10),
'StellarManageOfferOp': const(3),
'StellarPathPaymentOp': const(2),
'StellarPaymentOp': const(1),
'StellarSetOptionsOp': const(5),
}
op_wire_types = [
wire_types.StellarAccountMergeOp,
wire_types.StellarAllowTrustOp,
wire_types.StellarBumpSequenceOp,
wire_types.StellarChangeTrustOp,
wire_types.StellarCreateAccountOp,
wire_types.StellarCreatePassiveOfferOp,
wire_types.StellarManageDataOp,
wire_types.StellarManageOfferOp,
wire_types.StellarPathPaymentOp,
wire_types.StellarPaymentOp,
wire_types.StellarSetOptionsOp,
]
ASSET_TYPE_CREDIT_ALPHANUM4 = const(1)
ASSET_TYPE_CREDIT_ALPHANUM12 = const(2)
def get_op_code(msg) -> int:
if msg.__qualname__ not in op_codes:
raise ValueError('Stellar: op code unknown')
return op_codes[msg.__qualname__]

View File

@ -0,0 +1,192 @@
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp
from trezor.messages.StellarAssetType import StellarAssetType
from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp
from trezor.messages.StellarBumpSequenceOp import StellarBumpSequenceOp
from trezor.messages.StellarChangeTrustOp import StellarChangeTrustOp
from trezor.messages.StellarCreateAccountOp import StellarCreateAccountOp
from trezor.messages.StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp
from trezor.messages.StellarManageDataOp import StellarManageDataOp
from trezor.messages.StellarManageOfferOp import StellarManageOfferOp
from trezor.messages.StellarPathPaymentOp import StellarPathPaymentOp
from trezor.messages.StellarPaymentOp import StellarPaymentOp
from trezor.messages.StellarSetOptionsOp import StellarSetOptionsOp
from apps.stellar.consts import *
from apps.stellar.writers import *
# todo layout
def serialize_op(w, op):
_serialize_account(w, op.source_account)
write_uint32(w, get_op_code(op))
if isinstance(op, StellarAccountMergeOp):
serialize_account_merge_op(w, op)
elif isinstance(op, StellarAllowTrustOp):
serialize_allow_trust_op(w, op)
elif isinstance(op, StellarBumpSequenceOp):
serialize_bump_sequence_op(w, op)
elif isinstance(op, StellarChangeTrustOp):
serialize_change_trust_op(w, op)
elif isinstance(op, StellarCreateAccountOp):
serialize_create_account_op(w, op)
elif isinstance(op, StellarCreatePassiveOfferOp):
serialize_create_passive_offer_op(w, op)
elif isinstance(op, StellarManageDataOp):
serialize_manage_data_op(w, op)
elif isinstance(op, StellarManageOfferOp):
serialize_manage_offer_op(w, op)
elif isinstance(op, StellarPathPaymentOp):
serialize_path_payment_op(w, op)
elif isinstance(op, StellarPaymentOp):
serialize_payment_op(w, op)
elif isinstance(op, StellarSetOptionsOp):
serialize_set_options_op(w, op)
else:
raise ValueError('Stellar: unknown operation')
def serialize_account_merge_op(w, msg: StellarAccountMergeOp):
write_pubkey(w, msg.destination_account)
def serialize_allow_trust_op(w, msg: StellarAllowTrustOp):
# trustor account (the account being allowed to access the asset)
write_pubkey(w, msg.trusted_account)
write_uint32(w, msg.asset_type)
_serialize_asset_code(w, msg.asset_type, msg.asset_code)
write_bool(w, msg.is_authorized)
def serialize_bump_sequence_op(w, msg: StellarBumpSequenceOp):
write_uint64(w, msg.bump_to)
def serialize_change_trust_op(w, msg: StellarChangeTrustOp):
_serialize_asset(w, msg.asset)
write_uint64(w, msg.limit)
def serialize_create_account_op(w, msg: StellarCreateAccountOp):
write_pubkey(w, msg.new_account)
write_uint64(w, msg.starting_balance)
def serialize_create_passive_offer_op(w, msg: StellarCreatePassiveOfferOp):
_serialize_asset(w, msg.selling_asset)
_serialize_asset(w, msg.buying_asset)
write_uint64(w, msg.amount)
write_uint32(w, msg.price_n)
write_uint32(w, msg.price_d)
def serialize_manage_data_op(w, msg: StellarManageDataOp):
if len(msg.key) > 64:
raise ValueError('Stellar: max length of a key is 64 bytes')
write_string(w, msg.key)
write_bool(w, bool(msg.value))
if msg.value:
write_uint32(w, len(msg.value))
write_bytes(w, msg.value)
def serialize_manage_offer_op(w, msg: StellarManageOfferOp):
_serialize_asset(w, msg.selling_asset)
_serialize_asset(w, msg.buying_asset)
write_uint64(w, msg.amount) # amount to sell
write_uint32(w, msg.price_n) # numerator
write_uint32(w, msg.price_d) # denominator
write_uint64(w, msg.offer_id)
def serialize_path_payment_op(w, msg: StellarPathPaymentOp):
_serialize_asset(w, msg.send_asset)
write_uint64(w, msg.send_max)
write_pubkey(w, msg.destination_account)
_serialize_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_amount)
write_uint32(w, len(msg.paths))
for p in msg.paths:
_serialize_asset(w, p)
def serialize_payment_op(w, msg: StellarPaymentOp):
write_pubkey(w, msg.destination_account)
_serialize_asset(w, msg.asset)
write_uint64(w, msg.amount)
def serialize_set_options_op(w, msg: StellarSetOptionsOp):
# inflation destination
write_bool(w, bool(msg.inflation_destination_account))
if msg.inflation_destination_account:
write_pubkey(w, msg.inflation_destination_account)
# clear flags
write_bool(w, bool(msg.clear_flags))
if msg.clear_flags:
write_uint32(w, msg.clear_flags)
# set flags
write_bool(w, bool(msg.set_flags))
if msg.set_flags:
write_uint32(w, msg.set_flags)
# account thresholds
write_bool(w, bool(msg.master_weight))
if msg.master_weight:
write_uint32(w, msg.master_weight)
write_bool(w, bool(msg.low_threshold))
if msg.low_threshold:
write_uint32(w, msg.low_threshold)
write_bool(w, bool(msg.medium_threshold))
if msg.medium_threshold:
write_uint32(w, msg.medium_threshold)
write_bool(w, bool(msg.high_threshold))
if msg.high_threshold:
write_uint32(w, msg.high_threshold)
# home domain
write_bool(w, bool(msg.home_domain))
if msg.home_domain:
if len(msg.home_domain) > 32:
raise ValueError('Stellar: max length of a home domain is 32 bytes')
write_string(w, msg.home_domain)
# signer
write_bool(w, bool(msg.signer_type))
if msg.signer_type:
# signer type
write_uint32(w, msg.signer_type)
write_bytes(w, msg.signer_key)
write_uint32(w, msg.signer_weight)
def _serialize_account(w, source_account: bytes):
if source_account is None:
write_bool(w, False)
return
write_pubkey(w, source_account)
def _serialize_asset_code(w, asset_type: int, asset_code: str):
code = bytearray(asset_code)
if asset_type == ASSET_TYPE_CREDIT_ALPHANUM4:
# pad with zeros to 4 chars
write_bytes(w, code + bytearray([0] * (4 - len(code))))
elif asset_type == ASSET_TYPE_CREDIT_ALPHANUM12:
# pad with zeros to 12 chars
write_bytes(w, code + bytearray([0] * (12 - len(code))))
else:
raise ValueError('Stellar: invalid asset type')
def _serialize_asset(w, asset: StellarAssetType):
write_uint32(w, asset.type)
_serialize_asset_code(w, asset.type, asset.code)
write_pubkey(w, asset.issuer)

View File

@ -1,10 +1,10 @@
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp from apps.common import seed
from apps.stellar.writers import *
from apps.stellar.operations import serialize_op
from apps.stellar.consts import op_wire_types
from trezor.messages.StellarSignTx import StellarSignTx from trezor.messages.StellarSignTx import StellarSignTx
from trezor.messages.StellarTxOpRequest import StellarTxOpRequest from trezor.messages.StellarTxOpRequest import StellarTxOpRequest
from trezor.messages.StellarSignedTx import StellarSignedTx from trezor.messages.StellarSignedTx import StellarSignedTx
from trezor.messages import wire_types
from .writers import *
from ..common import seed
from trezor.crypto.curve import ed25519 from trezor.crypto.curve import ed25519
from trezor.crypto.hashlib import sha256 from trezor.crypto.hashlib import sha256
@ -18,15 +18,18 @@ async def sign_tx_loop(ctx, msg: StellarSignTx):
while True: while True:
req = signer.send(res) req = signer.send(res)
if isinstance(req, StellarTxOpRequest): if isinstance(req, StellarTxOpRequest):
res = await ctx.call(req, wire_types.StellarAccountMergeOp) res = await ctx.call(req, *op_wire_types)
elif isinstance(req, StellarSignedTx): elif isinstance(req, StellarSignedTx):
break break
else: else:
raise TypeError('Invalid Stellar signing instruction') raise TypeError('Stellar: Invalid signing instruction')
return req return req
async def sign_tx(ctx, msg): async def sign_tx(ctx, msg):
if msg.num_operations == 0:
raise ValueError('Stellar: At least one operation is required')
network_passphrase_hash = sha256(msg.network_passphrase).digest() network_passphrase_hash = sha256(msg.network_passphrase).digest()
# Stellar transactions consist of sha256 of: # Stellar transactions consist of sha256 of:
@ -57,7 +60,9 @@ async def sign_tx(ctx, msg):
write_uint32(w, msg.memo_type) write_uint32(w, msg.memo_type)
if msg.memo_type == 1: # nothing is needed for memo_type = 0 if msg.memo_type == 1: # nothing is needed for memo_type = 0
# Text: 4 bytes (size) + up to 28 bytes # Text: 4 bytes (size) + up to 28 bytes
write_bytes(w, bytearray(msg.memo_text)) # todo trim to 28 bytes? yes max 28 bytes! if len(msg.memo_text) > 28:
raise ValueError('Stellar: max length of a memo text is 28 bytes')
write_string(w, msg.memo_text)
elif msg.memo_type == 2: elif msg.memo_type == 2:
# ID: 64 bit unsigned integer # ID: 64 bit unsigned integer
write_uint64(w, msg.memo_id) write_uint64(w, msg.memo_id)
@ -68,10 +73,7 @@ async def sign_tx(ctx, msg):
write_uint32(w, msg.num_operations) write_uint32(w, msg.num_operations)
for i in range(msg.num_operations): for i in range(msg.num_operations):
op = yield StellarTxOpRequest() op = yield StellarTxOpRequest()
# todo ask serialize_op(w, op)
# todo serialize OP
if isinstance(op, StellarAccountMergeOp):
serialize_account_merge_op(w, op)
# # Determine what type of network this transaction is for - todo used for layout # # Determine what type of network this transaction is for - todo used for layout
# if msg.network_passphrase == "Public Global Stellar Network ; September 2015": # if msg.network_passphrase == "Public Global Stellar Network ; September 2015":
@ -98,14 +100,6 @@ async def sign_tx(ctx, msg):
yield resp yield resp
def serialize_account_merge_op(w, msg: StellarAccountMergeOp):
if not msg.source_account:
write_bool(w, False) # todo move this to stellar_confirmSourceAccount
#else: todo ask and hash the address
write_uint32(w, 8) # merge op todo
write_pubkey(w, msg.destination_account)
def node_derive(root, address_n: list): def node_derive(root, address_n: list):
node = root.clone() node = root.clone()
node.derive_path(address_n) node.derive_path(address_n)

View File

@ -9,6 +9,15 @@ def write_uint64(w, n: int):
write_bytes(w, ustruct.pack('>Q', n)) write_bytes(w, ustruct.pack('>Q', n))
def write_string(w, s: str):
write_uint32(w, len(s))
write_bytes(w, bytearray(s))
# if len isn't a multiple of 4, add padding bytes
reminder = len(s) % 4
if reminder:
write_bytes(w, bytearray([0] * (4 - reminder)))
def write_bytes(w, buf: bytearray): def write_bytes(w, buf: bytearray):
w.extend(buf) w.extend(buf)