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

stellar: layout

This commit is contained in:
Tomas Susanka 2018-05-29 12:16:28 +02:00
parent 1d3490a278
commit 44ce832163
5 changed files with 131 additions and 44 deletions

View File

@ -33,6 +33,14 @@ op_wire_types = [
ASSET_TYPE_CREDIT_ALPHANUM4 = const(1) ASSET_TYPE_CREDIT_ALPHANUM4 = const(1)
ASSET_TYPE_CREDIT_ALPHANUM12 = const(2) ASSET_TYPE_CREDIT_ALPHANUM12 = const(2)
# https://www.stellar.org/developers/guides/concepts/accounts.html#balance
# https://github.com/stellar/go/blob/master/amount/main.go
AMOUNT_DIVISIBILITY = const(7)
# https://github.com/stellar/go/blob/master/network/main.go
NETWORK_PASSPHRASE_PUBLIC = 'Public Global Stellar Network ; September 2015'
NETWORK_PASSPHRASE_TESTNET = 'Test SDF Network ; September 2015'
def get_op_code(msg) -> int: def get_op_code(msg) -> int:
if msg.__qualname__ not in op_codes: if msg.__qualname__ not in op_codes:

View File

@ -1,13 +1,12 @@
from apps.common import seed from apps.common import seed
from apps.common.confirm import confirm from apps.common.confirm import confirm
from apps.stellar import helpers
from trezor import ui from trezor import ui
from trezor.messages.StellarPublicKey import StellarPublicKey from trezor.messages.StellarPublicKey import StellarPublicKey
from trezor.messages.StellarGetPublicKey import StellarGetPublicKey from trezor.messages.StellarGetPublicKey import StellarGetPublicKey
from trezor.messages import ButtonRequestType from trezor.messages import ButtonRequestType
from trezor.ui.text import Text from trezor.ui.text import Text
from trezor.utils import chunks from trezor.utils import chunks
from trezor.crypto import base32
import ustruct
STELLAR_CURVE = 'ed25519' STELLAR_CURVE = 'ed25519'
@ -17,7 +16,7 @@ async def get_public_key(ctx, msg: StellarGetPublicKey):
pubkey = seed.remove_ed25519_public_key_prefix(node.public_key()) # todo better? pubkey = seed.remove_ed25519_public_key_prefix(node.public_key()) # todo better?
while True: while True:
if await _show(ctx, _address_from_public_key(pubkey)): if await _show(ctx, helpers.address_from_public_key(pubkey)):
break break
return StellarPublicKey(public_key=pubkey) return StellarPublicKey(public_key=pubkey)
@ -39,35 +38,3 @@ async def _show(ctx, address: str):
def _split_address(address: str): def _split_address(address: str):
return chunks(address, 17) return chunks(address, 17)
def _address_from_public_key(pubkey: bytes):
"""Returns the base32-encoded version of public key bytes (G...)"""
address = bytearray()
address.append(6 << 3) # version -> 'G'
address.extend(pubkey)
address.extend(ustruct.pack("<H", _crc16_checksum(address))) # checksum
return base32.encode(address)
def _crc16_checksum(data: bytearray):
"""Returns the CRC-16 checksum of bytearray bytes
Ported from Java implementation at: http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html
Initial value changed to 0x0000 to match Stellar configuration.
"""
crc = 0x0000
polynomial = 0x1021
for byte in data:
for i in range(8):
bit = ((byte >> (7 - i) & 1) == 1)
c15 = ((crc >> 15 & 1) == 1)
crc <<= 1
if c15 ^ bit:
crc ^= polynomial
return crc & 0xffff

View File

@ -0,0 +1,56 @@
from trezor.crypto import base32
import ustruct
class UiConfirmInit:
def __init__(self, pubkey: bytes, network: str):
self.pubkey = pubkey
self.network = network
class UiConfirmFinal:
def __init__(self, fee: int, num_operations: int):
self.fee = fee
self.num_operations = num_operations
def confirm_init(pubkey: bytes, network: str):
return (yield UiConfirmInit(pubkey, network))
def confirm_final(fee: int, num_operations: int):
return (yield UiConfirmFinal(fee, num_operations))
def address_from_public_key(pubkey: bytes):
"""Returns the base32-encoded version of public key bytes (G...)"""
address = bytearray()
address.append(6 << 3) # version -> 'G'
address.extend(pubkey)
address.extend(ustruct.pack("<H", _crc16_checksum(address))) # checksum
return base32.encode(address)
def _crc16_checksum(data: bytearray):
"""Returns the CRC-16 checksum of bytearray bytes
Ported from Java implementation at: http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html
Initial value changed to 0x0000 to match Stellar configuration.
"""
crc = 0x0000
polynomial = 0x1021
for byte in data:
for i in range(8):
bit = ((byte >> (7 - i) & 1) == 1)
c15 = ((crc >> 15 & 1) == 1)
crc <<= 1
if c15 ^ bit:
crc ^= polynomial
return crc & 0xffff

View File

@ -0,0 +1,55 @@
from apps.common.confirm import *
from apps.stellar.consts import AMOUNT_DIVISIBILITY
from apps.stellar.consts import NETWORK_PASSPHRASE_PUBLIC
from apps.stellar.consts import NETWORK_PASSPHRASE_TESTNET
from apps.stellar import helpers
from trezor import ui
from trezor.messages import ButtonRequestType
from trezor.ui.text import Text
from trezor.utils import chunks, format_amount
async def require_confirm_init(ctx, pubkey: bytes, network_passphrase: str):
content = Text('Confirm Stellar', ui.ICON_SEND,
ui.NORMAL, 'Initialize singing with',
ui.MONO, *format_address(pubkey),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
network = get_network_warning(network_passphrase)
if network:
content = Text('Confirm network', ui.ICON_CONFIRM,
ui.NORMAL, 'Transaction is on',
ui.BOLD, network,
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def require_confirm_final(ctx, fee: int, num_operations: int):
op_str = str(num_operations) + ' operation'
if num_operations > 1:
op_str += 's'
content = Text('Final confirm', ui.ICON_SEND,
ui.NORMAL, 'Sign this transaction',
ui.NORMAL, 'made up of ' + op_str,
ui.BOLD, 'and pay ' + format_amount(fee, AMOUNT_DIVISIBILITY) + ' XLM',
ui.NORMAL, 'for fee?',
icon_color=ui.GREEN)
# we use SignTx, not ConfirmOutput, for compatibility with T1
await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx)
def format_address(pubkey: bytes) -> str:
address = helpers.address_from_public_key(pubkey)
return split_address(address)
def split_address(address):
return chunks(address, 17)
def get_network_warning(network_passphrase: str):
if network_passphrase == NETWORK_PASSPHRASE_PUBLIC:
return None
if network_passphrase == NETWORK_PASSPHRASE_TESTNET:
return 'testnet network'
return 'private network'

View File

@ -2,6 +2,8 @@ from apps.common import seed
from apps.stellar.writers import * from apps.stellar.writers import *
from apps.stellar.operations import serialize_op from apps.stellar.operations import serialize_op
from apps.stellar.consts import op_wire_types from apps.stellar.consts import op_wire_types
from apps.stellar.layout import require_confirm_init, require_confirm_final
from apps.stellar import helpers
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
@ -21,6 +23,10 @@ async def sign_tx_loop(ctx, msg: StellarSignTx):
res = await ctx.call(req, *op_wire_types) res = await ctx.call(req, *op_wire_types)
elif isinstance(req, StellarSignedTx): elif isinstance(req, StellarSignedTx):
break break
elif isinstance(req, helpers.UiConfirmInit):
res = await require_confirm_init(ctx, req.pubkey, req.network)
elif isinstance(req, helpers.UiConfirmFinal):
res = await require_confirm_final(ctx, req.fee, req.num_operations)
else: else:
raise TypeError('Stellar: Invalid signing instruction') raise TypeError('Stellar: Invalid signing instruction')
return req return req
@ -77,18 +83,13 @@ async def sign_tx(ctx, msg):
op = yield StellarTxOpRequest() op = yield StellarTxOpRequest()
serialize_op(w, op) serialize_op(w, op)
# # Determine what type of network this transaction is for - todo used for layout
# if msg.network_passphrase == "Public Global Stellar Network ; September 2015":
# network_type = 1
# elif msg.network_passphrase == "Test SDF Network ; September 2015":
# network_type = 2
# else:
# network_type = 3
# # todo use network_type in layout
# 4 null bytes representing a (currently unused) empty union # 4 null bytes representing a (currently unused) empty union
write_uint32(w, 0) write_uint32(w, 0)
# confirms
await helpers.confirm_init(pubkey, msg.network_passphrase)
await helpers.confirm_final(msg.fee, msg.num_operations)
# sign # sign
# (note that the signature does not include the 4-byte hint since it can be calculated from the public key) # (note that the signature does not include the 4-byte hint since it can be calculated from the public key)
digest = sha256(w).digest() digest = sha256(w).digest()