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_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:
|
||||||
|
@ -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
|
|
||||||
|
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.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()
|
||||||
|
Loading…
Reference in New Issue
Block a user