stellar: layout

pull/25/head
Tomas Susanka 6 years ago
parent 1d3490a278
commit 44ce832163

@ -33,6 +33,14 @@ op_wire_types = [
ASSET_TYPE_CREDIT_ALPHANUM4 = const(1)
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:
if msg.__qualname__ not in op_codes:

@ -1,13 +1,12 @@
from apps.common import seed
from apps.common.confirm import confirm
from apps.stellar import helpers
from trezor import ui
from trezor.messages.StellarPublicKey import StellarPublicKey
from trezor.messages.StellarGetPublicKey import StellarGetPublicKey
from trezor.messages import ButtonRequestType
from trezor.ui.text import Text
from trezor.utils import chunks
from trezor.crypto import base32
import ustruct
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?
while True:
if await _show(ctx, _address_from_public_key(pubkey)):
if await _show(ctx, helpers.address_from_public_key(pubkey)):
break
return StellarPublicKey(public_key=pubkey)
@ -39,35 +38,3 @@ async def _show(ctx, address: str):
def _split_address(address: str):
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

@ -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

@ -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'

@ -2,6 +2,8 @@ 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 apps.stellar.layout import require_confirm_init, require_confirm_final
from apps.stellar import helpers
from trezor.messages.StellarSignTx import StellarSignTx
from trezor.messages.StellarTxOpRequest import StellarTxOpRequest
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)
elif isinstance(req, StellarSignedTx):
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:
raise TypeError('Stellar: Invalid signing instruction')
return req
@ -77,18 +83,13 @@ async def sign_tx(ctx, msg):
op = yield StellarTxOpRequest()
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
write_uint32(w, 0)
# confirms
await helpers.confirm_init(pubkey, msg.network_passphrase)
await helpers.confirm_final(msg.fee, msg.num_operations)
# sign
# (note that the signature does not include the 4-byte hint since it can be calculated from the public key)
digest = sha256(w).digest()

Loading…
Cancel
Save