mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-29 10:58:21 +00:00
2e578572dd
Squashed commit of the following: commit 060563458fbc3b4a17f4d77ba5cd62d0c265c806 Author: matejcik <ja@matejcik.cz> Date: Fri May 10 16:16:19 2019 +0200 skip t1 in eos test commit f759089fef29501467b62bf1540715132a72c4cf Author: matejcik <ja@matejcik.cz> Date: Fri May 10 15:55:20 2019 +0200 make style commit 3ecdd5f77b331d7a6e5a46a10c79d80f214f31bd Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Thu May 9 22:05:41 2019 +0300 Refinements in asset to to string conversion function according to code review and test cases for amounts less than 1 commit 72e44a35bada76abdd94ab866c2113a6d9d85191 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Wed May 8 00:27:45 2019 +0300 Moved to input_flow rest of the tests, cleanup and styling commit 92f9acbabcbef44a6912b074a309393450f0c8de Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Tue May 7 21:47:12 2019 +0300 Fix for amounts less then 1 commit 8a0154f7432ab78e69a123202a97194d34c2a3cb Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 23:26:24 2019 +0300 removed unnecessary peace of code commit b25c15de3eb1df863760e81ca69f09094349c26e Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 23:16:57 2019 +0300 Fixed validate path parameters commit f0f6e7036a8b88d9c5c6b702a8d851e9a9bd3378 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 23:04:58 2019 +0300 Fixes commit 0c64d3814300df86d452975b2bd46fea13f512d2 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 22:19:51 2019 +0300 Fixed styling commit 41d1e77231e7da78fade9b2efa1b7d1980f0d3a8 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 22:13:58 2019 +0300 Changes to core, added CURVE to path validation commit c045b4554ee8e058dbfe35f715b003d0d85ab1d4 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon May 6 22:07:37 2019 +0300 Changes according to review commit 3f0e6cfd40e7d87dc3287bc3a0b2b9db5dea5377 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Mon Apr 29 21:37:16 2019 +0300 Added change to make expiration date timezone agnostic commit efdf44c326cc3f3137c447e798db5439b57c91fa Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Thu Apr 18 00:14:30 2019 +0300 changes according to code review commit 3b3723da8f8f536c7c370a14236ea81aac25080a Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Tue Apr 16 23:44:50 2019 +0300 Merged python to monorepo commit da6b0c683c29388e15c889ecea6e7f7471961a19 Author: Andriy Tkachyshyn <atkachyshyn@gmail.com> Date: Tue Apr 16 23:13:42 2019 +0300 Merged core to monorepo
333 lines
9.3 KiB
Python
333 lines
9.3 KiB
Python
from datetime import datetime
|
|
|
|
from . import messages
|
|
from .tools import CallException, b58decode, expect, session
|
|
|
|
|
|
def name_to_number(name):
|
|
length = len(name)
|
|
value = 0
|
|
|
|
for i in range(0, 13):
|
|
c = 0
|
|
if i < length and i < 13:
|
|
c = char_to_symbol(name[i])
|
|
|
|
if i < 12:
|
|
c &= 0x1F
|
|
c <<= 64 - 5 * (i + 1)
|
|
else:
|
|
c &= 0x0F
|
|
|
|
value |= c
|
|
|
|
return value
|
|
|
|
|
|
def char_to_symbol(c):
|
|
if c >= "a" and c <= "z":
|
|
return ord(c) - ord("a") + 6
|
|
elif c >= "1" and c <= "5":
|
|
return ord(c) - ord("1") + 1
|
|
else:
|
|
return 0
|
|
|
|
|
|
def parse_asset(asset):
|
|
amount_str, symbol_str = asset.split(" ")
|
|
|
|
# "-1.0000" => ["-1", "0000"] => -10000
|
|
amount_parts = amount_str.split(".", maxsplit=1)
|
|
amount = int("".join(amount_parts))
|
|
|
|
precision = 0
|
|
if len(amount_parts) > 1:
|
|
precision = len(amount_parts[1])
|
|
|
|
# 4, "EOS" => b"\x04EOS" => little-endian uint32
|
|
symbol_bytes = bytes([precision]) + symbol_str.encode()
|
|
symbol = int.from_bytes(symbol_bytes, "little")
|
|
|
|
return messages.EosAsset(amount=amount, symbol=symbol)
|
|
|
|
|
|
def public_key_to_buffer(pub_key):
|
|
_t = 0
|
|
if pub_key[:3] == "EOS":
|
|
pub_key = pub_key[3:]
|
|
_t = 0
|
|
elif pub_key[:7] == "PUB_K1_":
|
|
pub_key = pub_key[7:]
|
|
_t = 0
|
|
elif pub_key[:7] == "PUB_R1_":
|
|
pub_key = pub_key[7:]
|
|
_t = 1
|
|
|
|
return _t, b58decode(pub_key, None)[:-4]
|
|
|
|
|
|
def parse_common(action):
|
|
authorization = []
|
|
for auth in action["authorization"]:
|
|
authorization.append(
|
|
messages.EosPermissionLevel(
|
|
actor=name_to_number(auth["actor"]),
|
|
permission=name_to_number(auth["permission"]),
|
|
)
|
|
)
|
|
|
|
return messages.EosActionCommon(
|
|
account=name_to_number(action["account"]),
|
|
name=name_to_number(action["name"]),
|
|
authorization=authorization,
|
|
)
|
|
|
|
|
|
def parse_transfer(data):
|
|
return messages.EosActionTransfer(
|
|
sender=name_to_number(data["from"]),
|
|
receiver=name_to_number(data["to"]),
|
|
memo=data["memo"],
|
|
quantity=parse_asset(data["quantity"]),
|
|
)
|
|
|
|
|
|
def parse_vote_producer(data):
|
|
producers = []
|
|
for producer in data["producers"]:
|
|
producers.append(name_to_number(producer))
|
|
|
|
return messages.EosActionVoteProducer(
|
|
voter=name_to_number(data["account"]),
|
|
proxy=name_to_number(data["proxy"]),
|
|
producers=producers,
|
|
)
|
|
|
|
|
|
def parse_buy_ram(data):
|
|
return messages.EosActionBuyRam(
|
|
payer=name_to_number(data["payer"]),
|
|
receiver=name_to_number(data["receiver"]),
|
|
quantity=parse_asset(data["quant"]),
|
|
)
|
|
|
|
|
|
def parse_buy_rambytes(data):
|
|
return messages.EosActionBuyRamBytes(
|
|
payer=name_to_number(data["payer"]),
|
|
receiver=name_to_number(data["receiver"]),
|
|
bytes=int(data["bytes"]),
|
|
)
|
|
|
|
|
|
def parse_sell_ram(data):
|
|
return messages.EosActionSellRam(
|
|
account=name_to_number(data["account"]), bytes=int(data["bytes"])
|
|
)
|
|
|
|
|
|
def parse_delegate(data):
|
|
return messages.EosActionDelegate(
|
|
sender=name_to_number(data["sender"]),
|
|
receiver=name_to_number(data["receiver"]),
|
|
net_quantity=parse_asset(data["stake_net_quantity"]),
|
|
cpu_quantity=parse_asset(data["stake_cpu_quantity"]),
|
|
transfer=bool(data["transfer"]),
|
|
)
|
|
|
|
|
|
def parse_undelegate(data):
|
|
return messages.EosActionUndelegate(
|
|
sender=name_to_number(data["sender"]),
|
|
receiver=name_to_number(data["receiver"]),
|
|
net_quantity=parse_asset(data["unstake_net_quantity"]),
|
|
cpu_quantity=parse_asset(data["unstake_cpu_quantity"]),
|
|
)
|
|
|
|
|
|
def parse_refund(data):
|
|
return messages.EosActionRefund(owner=name_to_number(data["owner"]))
|
|
|
|
|
|
def parse_updateauth(data):
|
|
auth = parse_authorization(data["auth"])
|
|
|
|
return messages.EosActionUpdateAuth(
|
|
account=name_to_number(data["account"]),
|
|
permission=name_to_number(data["permission"]),
|
|
parent=name_to_number(data["parent"]),
|
|
auth=auth,
|
|
)
|
|
|
|
|
|
def parse_deleteauth(data):
|
|
return messages.EosActionDeleteAuth(
|
|
account=name_to_number(data["account"]),
|
|
permission=name_to_number(data["permission"]),
|
|
)
|
|
|
|
|
|
def parse_linkauth(data):
|
|
return messages.EosActionLinkAuth(
|
|
account=name_to_number(data["account"]),
|
|
code=name_to_number(data["code"]),
|
|
type=name_to_number(data["type"]),
|
|
requirement=name_to_number(data["requirement"]),
|
|
)
|
|
|
|
|
|
def parse_unlinkauth(data):
|
|
return messages.EosActionUnlinkAuth(
|
|
account=name_to_number(data["account"]),
|
|
code=name_to_number(data["code"]),
|
|
type=name_to_number(data["type"]),
|
|
)
|
|
|
|
|
|
def parse_authorization(data):
|
|
keys = []
|
|
for key in data["keys"]:
|
|
_t, _k = public_key_to_buffer(key["key"])
|
|
|
|
keys.append(
|
|
messages.EosAuthorizationKey(type=_t, key=_k, weight=int(key["weight"]))
|
|
)
|
|
|
|
accounts = []
|
|
for account in data["accounts"]:
|
|
accounts.append(
|
|
messages.EosAuthorizationAccount(
|
|
account=messages.EosPermissionLevel(
|
|
actor=name_to_number(account["permission"]["actor"]),
|
|
permission=name_to_number(account["permission"]["permission"]),
|
|
),
|
|
weight=int(account["weight"]),
|
|
)
|
|
)
|
|
|
|
waits = []
|
|
for wait in data["waits"]:
|
|
waits.append(
|
|
messages.EosAuthorizationWait(
|
|
wait_sec=int(wait["wait_sec"]), weight=int(wait["weight"])
|
|
)
|
|
)
|
|
|
|
return messages.EosAuthorization(
|
|
threshold=int(data["threshold"]), keys=keys, accounts=accounts, waits=waits
|
|
)
|
|
|
|
|
|
def parse_new_account(data):
|
|
owner = parse_authorization(data["owner"])
|
|
active = parse_authorization(data["active"])
|
|
|
|
return messages.EosActionNewAccount(
|
|
creator=name_to_number(data["creator"]),
|
|
name=name_to_number(data["name"]),
|
|
owner=owner,
|
|
active=active,
|
|
)
|
|
|
|
|
|
def parse_unknown(data):
|
|
data_bytes = bytes.fromhex(data)
|
|
return messages.EosActionUnknown(data_size=len(data_bytes), data_chunk=data_bytes)
|
|
|
|
|
|
def parse_action(action):
|
|
tx_action = messages.EosTxActionAck()
|
|
data = action["data"]
|
|
|
|
tx_action.common = parse_common(action)
|
|
|
|
if action["account"] == "eosio":
|
|
if action["name"] == "voteproducer":
|
|
tx_action.vote_producer = parse_vote_producer(data)
|
|
elif action["name"] == "buyram":
|
|
tx_action.buy_ram = parse_buy_ram(data)
|
|
elif action["name"] == "buyrambytes":
|
|
tx_action.buy_ram_bytes = parse_buy_rambytes(data)
|
|
elif action["name"] == "sellram":
|
|
tx_action.sell_ram = parse_sell_ram(data)
|
|
elif action["name"] == "delegatebw":
|
|
tx_action.delegate = parse_delegate(data)
|
|
elif action["name"] == "undelegatebw":
|
|
tx_action.undelegate = parse_undelegate(data)
|
|
elif action["name"] == "refund":
|
|
tx_action.refund = parse_refund(data)
|
|
elif action["name"] == "updateauth":
|
|
tx_action.update_auth = parse_updateauth(data)
|
|
elif action["name"] == "deleteauth":
|
|
tx_action.delete_auth = parse_deleteauth(data)
|
|
elif action["name"] == "linkauth":
|
|
tx_action.link_auth = parse_linkauth(data)
|
|
elif action["name"] == "unlinkauth":
|
|
tx_action.unlink_auth = parse_unlinkauth(data)
|
|
elif action["name"] == "newaccount":
|
|
tx_action.new_account = parse_new_account(data)
|
|
elif action["name"] == "transfer":
|
|
tx_action.transfer = parse_transfer(data)
|
|
else:
|
|
tx_action.unknown = parse_unknown(data)
|
|
|
|
return tx_action
|
|
|
|
|
|
def parse_transaction_json(transaction):
|
|
header = messages.EosTxHeader()
|
|
header.expiration = int(
|
|
(
|
|
datetime.strptime(transaction["expiration"], "%Y-%m-%dT%H:%M:%S")
|
|
- datetime(1970, 1, 1)
|
|
).total_seconds()
|
|
)
|
|
header.ref_block_num = int(transaction["ref_block_num"])
|
|
header.ref_block_prefix = int(transaction["ref_block_prefix"])
|
|
header.max_net_usage_words = int(transaction["net_usage_words"])
|
|
header.max_cpu_usage_ms = int(transaction["max_cpu_usage_ms"])
|
|
header.delay_sec = int(transaction["delay_sec"])
|
|
|
|
actions = [parse_action(a) for a in transaction["actions"]]
|
|
|
|
return header, actions
|
|
|
|
|
|
# ====== Client functions ====== #
|
|
|
|
|
|
@expect(messages.EosPublicKey)
|
|
def get_public_key(client, n, show_display=False, multisig=None):
|
|
response = client.call(
|
|
messages.EosGetPublicKey(address_n=n, show_display=show_display)
|
|
)
|
|
return response
|
|
|
|
|
|
@session
|
|
def sign_tx(client, address, transaction, chain_id):
|
|
header, actions = parse_transaction_json(transaction)
|
|
|
|
msg = messages.EosSignTx()
|
|
msg.address_n = address
|
|
msg.chain_id = bytes.fromhex(chain_id)
|
|
msg.header = header
|
|
msg.num_actions = len(actions)
|
|
|
|
response = client.call(msg)
|
|
|
|
try:
|
|
while isinstance(response, messages.EosTxActionRequest):
|
|
response = client.call(actions.pop(0))
|
|
except IndexError:
|
|
# pop from empty list
|
|
raise CallException(
|
|
"Eos.UnexpectedEndOfOperations",
|
|
"Reached end of operations without a signature.",
|
|
) from None
|
|
|
|
if not isinstance(response, messages.EosSignedTx):
|
|
raise CallException(messages.FailureType.UnexpectedMessage, response)
|
|
|
|
return response
|