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:
parent
00fb252063
commit
17a4ccd268
40
src/apps/stellar/consts.py
Normal file
40
src/apps/stellar/consts.py
Normal 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__]
|
192
src/apps/stellar/operations.py
Normal file
192
src/apps/stellar/operations.py
Normal 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)
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user