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:
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
|
||||
|
56
src/apps/stellar/helpers.py
Normal file
56
src/apps/stellar/helpers.py
Normal 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
|
55
src/apps/stellar/layout.py
Normal file
55
src/apps/stellar/layout.py
Normal 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'
|
@ -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…
Reference in New Issue
Block a user