1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-21 23:18:13 +00:00

add EOS support

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
This commit is contained in:
matejcik 2019-05-10 16:23:18 +02:00
parent 23306d2a0d
commit 2e578572dd
50 changed files with 3169 additions and 1 deletions

View File

@ -35,6 +35,7 @@ CPPDEFINES_MOD += [
('USE_MONERO', '1'),
('USE_CARDANO', '1'),
('USE_NEM', '1'),
('USE_EOS', '1'),
]
SOURCE_MOD += [
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',

View File

@ -34,6 +34,7 @@ CPPDEFINES_MOD += [
('USE_MONERO', '1'),
('USE_CARDANO', '1'),
('USE_NEM', '1'),
('USE_EOS', '1'),
]
SOURCE_MOD += [
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',

View File

@ -85,8 +85,17 @@ static int ethereum_is_canonical(uint8_t v, uint8_t signature[64]) {
return (v & 2) == 0;
}
static int eos_is_canonical(uint8_t v, uint8_t signature[64]) {
(void)v;
return !(signature[0] & 0x80) &&
!(signature[0] == 0 && !(signature[1] & 0x80)) &&
!(signature[32] & 0x80) &&
!(signature[32] == 0 && !(signature[33] & 0x80));
}
enum {
CANONICAL_SIG_ETHEREUM = 1,
CANONICAL_SIG_EOS = 2,
};
/// def sign(secret_key: bytes, digest: bytes, compressed: bool = True,
@ -106,6 +115,9 @@ STATIC mp_obj_t mod_trezorcrypto_secp256k1_sign(size_t n_args,
case CANONICAL_SIG_ETHEREUM:
is_canonical = ethereum_is_canonical;
break;
case CANONICAL_SIG_EOS:
is_canonical = eos_is_canonical;
break;
}
if (sk.len != 32) {
mp_raise_ValueError("Invalid length of secret key");
@ -236,6 +248,8 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_secp256k1_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_secp256k1_multiply_obj)},
{MP_ROM_QSTR(MP_QSTR_CANONICAL_SIG_ETHEREUM),
MP_OBJ_NEW_SMALL_INT(CANONICAL_SIG_ETHEREUM)},
{MP_ROM_QSTR(MP_QSTR_CANONICAL_SIG_EOS),
MP_OBJ_NEW_SMALL_INT(CANONICAL_SIG_EOS)},
};
STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_secp256k1_globals,
mod_trezorcrypto_secp256k1_globals_table);

View File

@ -20,8 +20,8 @@
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#if TREZOR_MODEL == T

13
core/src/apps/eos/__init__.py Executable file
View File

@ -0,0 +1,13 @@
from trezor import wire
from trezor.messages import MessageType
from apps.common import HARDENED
CURVE = "secp256k1"
def boot():
ns = [[CURVE, HARDENED | 44, HARDENED | 194]]
wire.add(MessageType.EosGetPublicKey, __name__, "get_public_key", ns)
wire.add(MessageType.EosSignTx, __name__, "sign_tx", ns)

View File

@ -0,0 +1,120 @@
from trezor.crypto.hashlib import sha256
from trezor.messages.EosTxActionRequest import EosTxActionRequest
from trezor.messages.MessageType import EosTxActionAck
from trezor.utils import HashWriter
from apps.eos import helpers, writers
from apps.eos.actions import layout
async def process_action(ctx, sha, action):
name = helpers.eos_name_to_string(action.common.name)
account = helpers.eos_name_to_string(action.common.account)
if not check_action(action, name, account):
raise ValueError("Invalid action")
w = bytearray()
if account == "eosio":
if name == "buyram":
await layout.confirm_action_buyram(ctx, action.buy_ram)
writers.write_action_buyram(w, action.buy_ram)
elif name == "buyrambytes":
await layout.confirm_action_buyrambytes(ctx, action.buy_ram_bytes)
writers.write_action_buyrambytes(w, action.buy_ram_bytes)
elif name == "sellram":
await layout.confirm_action_sellram(ctx, action.sell_ram)
writers.write_action_sellram(w, action.sell_ram)
elif name == "delegatebw":
await layout.confirm_action_delegate(ctx, action.delegate)
writers.write_action_delegate(w, action.delegate)
elif name == "undelegatebw":
await layout.confirm_action_undelegate(ctx, action.undelegate)
writers.write_action_undelegate(w, action.undelegate)
elif name == "refund":
await layout.confirm_action_refund(ctx, action.refund)
writers.write_action_refund(w, action.refund)
elif name == "voteproducer":
await layout.confirm_action_voteproducer(ctx, action.vote_producer)
writers.write_action_voteproducer(w, action.vote_producer)
elif name == "updateauth":
await layout.confirm_action_updateauth(ctx, action.update_auth)
writers.write_action_updateauth(w, action.update_auth)
elif name == "deleteauth":
await layout.confirm_action_deleteauth(ctx, action.delete_auth)
writers.write_action_deleteauth(w, action.delete_auth)
elif name == "linkauth":
await layout.confirm_action_linkauth(ctx, action.link_auth)
writers.write_action_linkauth(w, action.link_auth)
elif name == "unlinkauth":
await layout.confirm_action_unlinkauth(ctx, action.unlink_auth)
writers.write_action_unlinkauth(w, action.unlink_auth)
elif name == "newaccount":
await layout.confirm_action_newaccount(ctx, action.new_account)
writers.write_action_newaccount(w, action.new_account)
else:
raise ValueError("Unrecognized action type for eosio")
elif name == "transfer":
await layout.confirm_action_transfer(ctx, action.transfer, account)
writers.write_action_transfer(w, action.transfer)
else:
await process_unknown_action(ctx, w, action)
writers.write_action_common(sha, action.common)
writers.write_variant32(sha, len(w))
writers.write_bytes(sha, w)
async def process_unknown_action(ctx, w, action):
checksum = HashWriter(sha256())
writers.write_variant32(checksum, action.unknown.data_size)
checksum.extend(action.unknown.data_chunk)
writers.write_bytes(w, action.unknown.data_chunk)
bytes_left = action.unknown.data_size - len(action.unknown.data_chunk)
while bytes_left != 0:
action = await ctx.call(
EosTxActionRequest(data_size=bytes_left), EosTxActionAck
)
if action.unknown is None:
raise ValueError("Bad response. Unknown struct expected.")
checksum.extend(action.unknown.data_chunk)
writers.write_bytes(w, action.unknown.data_chunk)
bytes_left -= len(action.unknown.data_chunk)
if bytes_left < 0:
raise ValueError("Bad response. Buffer overflow.")
await layout.confirm_action_unknown(ctx, action.common, checksum.get_digest())
def check_action(action, name, account):
if account == "eosio":
if (
(name == "buyram" and action.buy_ram is not None)
or (name == "buyrambytes" and action.buy_ram_bytes is not None)
or (name == "sellram" and action.sell_ram is not None)
or (name == "delegatebw" and action.delegate is not None)
or (name == "undelegatebw" and action.undelegate is not None)
or (name == "refund" and action.refund is not None)
or (name == "voteproducer" and action.vote_producer is not None)
or (name == "updateauth" and action.update_auth is not None)
or (name == "deleteauth" and action.delete_auth is not None)
or (name == "linkauth" and action.link_auth is not None)
or (name == "unlinkauth" and action.unlink_auth is not None)
or (name == "newaccount" and action.new_account is not None)
):
return True
else:
return False
elif name == "transfer":
return action.transfer is not None
elif action.unknown is not None:
return True
return False

View File

@ -0,0 +1,411 @@
from micropython import const
from ubinascii import hexlify
from trezor import ui, wire
from trezor.messages import (
ButtonRequestType,
EosActionBuyRam,
EosActionBuyRamBytes,
EosActionDelegate,
EosActionDeleteAuth,
EosActionLinkAuth,
EosActionNewAccount,
EosActionRefund,
EosActionSellRam,
EosActionTransfer,
EosActionUndelegate,
EosActionUnlinkAuth,
EosActionUpdateAuth,
EosActionVoteProducer,
MessageType,
)
from trezor.messages.ButtonRequest import ButtonRequest
from trezor.ui.confirm import CONFIRMED, ConfirmDialog
from trezor.ui.scroll import Scrollpage, animate_swipe, paginate
from trezor.ui.text import Text
from trezor.utils import chunks
from apps.eos import helpers
from apps.eos.get_public_key import _public_key_to_wif
from apps.eos.layout import require_confirm
_LINE_LENGTH = const(17)
_LINE_PLACEHOLDER = "{:<" + str(_LINE_LENGTH) + "}"
_FIRST_PAGE = const(0)
_TWO_FIELDS_PER_PAGE = const(2)
_THREE_FIELDS_PER_PAGE = const(3)
_FOUR_FIELDS_PER_PAGE = const(4)
_FIVE_FIELDS_PER_PAGE = const(5)
async def confirm_action_buyram(ctx, msg: EosActionBuyRam):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Buy RAM"
fields = []
fields.append("Payer:")
fields.append(helpers.eos_name_to_string(msg.payer))
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.receiver))
fields.append("Amount:")
fields.append(helpers.eos_asset_to_string(msg.quantity))
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_buyrambytes(ctx, msg: EosActionBuyRamBytes):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Buy RAM"
fields = []
fields.append("Payer:")
fields.append(helpers.eos_name_to_string(msg.payer))
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.receiver))
fields.append("Bytes:")
fields.append(str(msg.bytes))
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_delegate(ctx, msg: EosActionDelegate):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Delegate"
fields = []
fields.append("Sender:")
fields.append(helpers.eos_name_to_string(msg.sender))
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.receiver))
fields.append("CPU:")
fields.append(helpers.eos_asset_to_string(msg.cpu_quantity))
fields.append("NET:")
fields.append(helpers.eos_asset_to_string(msg.net_quantity))
if msg.transfer:
fields.append("Transfer: Yes")
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.receiver))
else:
fields.append("Transfer: No")
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_sellram(ctx, msg: EosActionSellRam):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Sell RAM"
fields = []
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.account))
fields.append("Bytes:")
fields.append(str(msg.bytes))
pages = list(chunks(fields, _TWO_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_undelegate(ctx, msg: EosActionUndelegate):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Undelegate"
fields = []
fields.append("Sender:")
fields.append(helpers.eos_name_to_string(msg.sender))
fields.append("Receiver:")
fields.append(helpers.eos_name_to_string(msg.receiver))
fields.append("CPU:")
fields.append(helpers.eos_asset_to_string(msg.cpu_quantity))
fields.append("NET:")
fields.append(helpers.eos_asset_to_string(msg.net_quantity))
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_refund(ctx, msg: EosActionRefund):
text = Text("Refund", ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.normal("Owner:")
text.normal(helpers.eos_name_to_string(msg.owner))
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
async def confirm_action_voteproducer(ctx, msg: EosActionVoteProducer):
if msg.proxy and not msg.producers:
# PROXY
text = Text("Vote for proxy", ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.normal("Voter:")
text.normal(helpers.eos_name_to_string(msg.voter))
text.normal("Proxy:")
text.normal(helpers.eos_name_to_string(msg.proxy))
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
elif msg.producers:
# PRODUCERS
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
producers = list(enumerate(msg.producers))
pages = list(chunks(producers, _FIVE_FIELDS_PER_PAGE))
paginator = paginate(show_voter_page, len(pages), _FIRST_PAGE, pages)
await ctx.wait(paginator)
else:
# Cancel vote
text = Text("Cancel vote", ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.normal("Voter:")
text.normal(helpers.eos_name_to_string(msg.voter))
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
async def confirm_action_transfer(ctx, msg: EosActionTransfer, account: str):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Transfer"
fields = []
fields.append("From:")
fields.append(helpers.eos_name_to_string(msg.sender))
fields.append("To:")
fields.append(helpers.eos_name_to_string(msg.receiver))
fields.append("Amount:")
fields.append(helpers.eos_asset_to_string(msg.quantity))
fields.append("Contract:")
fields.append(account)
if msg.memo is not None:
fields.append("Memo:")
fields += split_data(msg.memo[:512])
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_updateauth(ctx, msg: EosActionUpdateAuth):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Update Auth"
fields = []
fields.append("Account:")
fields.append(helpers.eos_name_to_string(msg.account))
fields.append("Permission:")
fields.append(helpers.eos_name_to_string(msg.permission))
fields.append("Parent:")
fields.append(helpers.eos_name_to_string(msg.parent))
fields += authorization_fields(msg.auth)
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_deleteauth(ctx, msg: EosActionDeleteAuth):
text = Text("Delete auth", ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.normal("Account:")
text.normal(helpers.eos_name_to_string(msg.account))
text.normal("Permission:")
text.normal(helpers.eos_name_to_string(msg.permission))
await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
async def confirm_action_linkauth(ctx, msg: EosActionLinkAuth):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Link Auth"
fields = []
fields.append("Account:")
fields.append(helpers.eos_name_to_string(msg.account))
fields.append("Code:")
fields.append(helpers.eos_name_to_string(msg.code))
fields.append("Type:")
fields.append(helpers.eos_name_to_string(msg.type))
fields.append("Requirement:")
fields.append(helpers.eos_name_to_string(msg.requirement))
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_unlinkauth(ctx, msg: EosActionUnlinkAuth):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Unlink Auth"
fields = []
fields.append("Account:")
fields.append(helpers.eos_name_to_string(msg.account))
fields.append("Code:")
fields.append(helpers.eos_name_to_string(msg.code))
fields.append("Type:")
fields.append(helpers.eos_name_to_string(msg.type))
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_newaccount(ctx, msg: EosActionNewAccount):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "New Account"
fields = []
fields.append("Creator:")
fields.append(helpers.eos_name_to_string(msg.creator))
fields.append("Name:")
fields.append(helpers.eos_name_to_string(msg.name))
fields += authorization_fields(msg.owner)
fields += authorization_fields(msg.active)
pages = list(chunks(fields, _FOUR_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
async def confirm_action_unknown(ctx, action, checksum):
await ctx.call(
ButtonRequest(code=ButtonRequestType.ConfirmOutput), MessageType.ButtonAck
)
text = "Arbitrary data"
fields = []
fields.append("Contract:")
fields.append(helpers.eos_name_to_string(action.account))
fields.append("Action Name:")
fields.append(helpers.eos_name_to_string(action.name))
fields.append("Checksum: ")
fields += split_data(hexlify(checksum).decode("ascii"))
pages = list(chunks(fields, _FIVE_FIELDS_PER_PAGE))
paginator = paginate(show_lines_page, len(pages), _FIRST_PAGE, pages, text)
await ctx.wait(paginator)
@ui.layout
async def show_lines_page(page: int, page_count: int, pages: list, header: str):
if header == "Arbitrary data":
text = Text(header, ui.ICON_WIPE, icon_color=ui.RED)
else:
text = Text(header, ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.mono(*pages[page])
content = Scrollpage(text, page, page_count)
if page + 1 == page_count:
if await ConfirmDialog(content) != CONFIRMED:
raise wire.ActionCancelled("Action cancelled")
else:
content.render()
await animate_swipe()
@ui.layout
async def show_voter_page(page: int, page_count: int, pages: list):
lines = [
"{:2d}. {}".format(wi + 1, helpers.eos_name_to_string(producer))
for wi, producer in pages[page]
]
text = Text("Vote for producers", ui.ICON_CONFIRM, icon_color=ui.GREEN)
text.mono(*lines)
content = Scrollpage(text, page, page_count)
if page + 1 == page_count:
if await ConfirmDialog(content) != CONFIRMED:
raise wire.ActionCancelled("Action cancelled")
else:
content.render()
await animate_swipe()
def authorization_fields(auth):
fields = []
fields.append("Threshold:")
fields.append(str(auth.threshold))
for i, key in enumerate(auth.keys):
_key = _public_key_to_wif(bytes(key.key))
_weight = str(key.weight)
header = "Key #{}:".format(i + 1)
w_header = "Key #{} Weight:".format(i + 1)
fields.append(header)
fields += split_data(_key)
fields.append(w_header)
fields.append(_weight)
for i, account in enumerate(auth.accounts):
_account = helpers.eos_name_to_string(account.account.actor)
_permission = helpers.eos_name_to_string(account.account.permission)
a_header = "Account #{}:".format(i + 1)
p_header = "Acc Permission #{}:".format(i + 1)
w_header = "Account #{} weight:".format(i + 1)
fields.append(a_header)
fields.append(_account)
fields.append(p_header)
fields.append(_permission)
fields.append(w_header)
fields.append(str(account.weight))
for i, wait in enumerate(auth.waits):
_wait = str(wait.wait_sec)
_weight = str(wait.weight)
header = "Delay #{}".format(i + 1)
w_header = "Delay #{} weight:".format(i + 1)
fields.append(header)
fields.append("{} sec".format(_wait))
fields.append(w_header)
fields.append(_weight)
return fields
def split_data(data):
temp_list = []
len_left = len(data)
while len_left > 0:
temp_list.append("{} ".format(data[:_LINE_LENGTH]))
data = data[_LINE_LENGTH:]
len_left = len(data)
return temp_list

View File

@ -0,0 +1,43 @@
from trezor import wire
from trezor.crypto import base58
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import ripemd160
from trezor.messages.EosGetPublicKey import EosGetPublicKey
from trezor.messages.EosPublicKey import EosPublicKey
from apps.common import paths
from apps.eos import CURVE
from apps.eos.helpers import validate_full_path
from apps.eos.layout import require_get_public_key
def _ripemd160_32(data: bytes) -> bytes:
return ripemd160(data).digest()[:4]
def _public_key_to_wif(pub_key: bytes) -> str:
if len(pub_key) == 65:
head = 0x03 if pub_key[64] & 0x01 else 0x02
compresed_pub_key = bytes([head]) + pub_key[1:33]
elif len(pub_key) == 33:
compresed_pub_key = pub_key
else:
raise wire.DataError("invalid public key length")
return "EOS" + base58.encode_check(compresed_pub_key, _ripemd160_32)
def _get_public_key(node):
seckey = node.private_key()
public_key = secp256k1.publickey(seckey, True)
wif = _public_key_to_wif(public_key)
return wif, public_key
async def get_public_key(ctx, msg: EosGetPublicKey, keychain):
await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE)
node = keychain.derive(msg.address_n)
wif, public_key = _get_public_key(node)
if msg.show_display:
await require_get_public_key(ctx, wif)
return EosPublicKey(wif, public_key)

View File

@ -0,0 +1,54 @@
from trezor.messages import EosAsset
from apps.common import HARDENED
def eos_name_to_string(value) -> str:
charmap = ".12345abcdefghijklmnopqrstuvwxyz"
tmp = value
string = ""
for i in range(0, 13):
c = charmap[tmp & (0x0F if i == 0 else 0x1F)]
string = c + string
tmp >>= 4 if i == 0 else 5
return string.rstrip(".")
def eos_asset_to_string(asset: EosAsset) -> str:
symbol_bytes = int.to_bytes(asset.symbol, 8, "big")
precision = symbol_bytes[7]
symbol = bytes(reversed(symbol_bytes[:7])).rstrip(b"\x00").decode("ascii")
amount_digits = "{:0{precision}d}".format(asset.amount, precision=precision)
if precision > 0:
integer = amount_digits[:-precision]
if integer == "":
integer = "0"
fraction = amount_digits[-precision:]
return "{}.{} {}".format(integer, fraction, symbol)
else:
return "{} {}".format(amount_digits, symbol)
def validate_full_path(path: list) -> bool:
"""
Validates derivation path to equal 44'/194'/a'/0/0,
where `a` is an account index from 0 to 1 000 000.
Similar to Ethereum this should be 44'/194'/a', but for
compatibility with other HW vendors we use 44'/194'/a'/0/0.
"""
if len(path) != 5:
return False
if path[0] != 44 | HARDENED:
return False
if path[1] != 194 | HARDENED:
return False
if path[2] < HARDENED or path[2] > 1000000 | HARDENED:
return False
if path[3] != 0:
return False
if path[4] != 0:
return False
return True

View File

@ -0,0 +1,19 @@
from trezor import ui
from trezor.messages import ButtonRequestType
from trezor.ui.text import Text
from apps.common.confirm import require_confirm
async def require_get_public_key(ctx, public_key):
text = Text("Confirm public key", ui.ICON_RECEIVE, icon_color=ui.GREEN)
text.normal(public_key)
return await require_confirm(ctx, text, code=ButtonRequestType.PublicKey)
async def require_sign_tx(ctx, num_actions):
text = Text("Sign transaction", ui.ICON_SEND, icon_color=ui.GREEN)
text.normal("You are about")
text.normal("to sign {}".format(num_actions))
text.normal("action(s).")
return await require_confirm(ctx, text, code=ButtonRequestType.SignTx)

View File

@ -0,0 +1,54 @@
from trezor import wire
from trezor.crypto.curve import secp256k1
from trezor.crypto.hashlib import sha256
from trezor.messages.EosSignedTx import EosSignedTx
from trezor.messages.EosSignTx import EosSignTx
from trezor.messages.EosTxActionRequest import EosTxActionRequest
from trezor.messages.MessageType import EosTxActionAck
from trezor.utils import HashWriter
from apps.common import paths
from apps.eos import CURVE, writers
from apps.eos.actions import process_action
from apps.eos.helpers import validate_full_path
from apps.eos.layout import require_sign_tx
async def sign_tx(ctx, msg: EosSignTx, keychain):
if msg.chain_id is None:
raise wire.DataError("No chain id")
if msg.header is None:
raise wire.DataError("No header")
if msg.num_actions is None or msg.num_actions == 0:
raise wire.DataError("No actions")
await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE)
node = keychain.derive(msg.address_n)
sha = HashWriter(sha256())
await _init(ctx, sha, msg)
await _actions(ctx, sha, msg.num_actions)
writers.write_variant32(sha, 0)
writers.write_bytes(sha, bytearray(32))
digest = sha.get_digest()
signature = secp256k1.sign(
node.private_key(), digest, True, secp256k1.CANONICAL_SIG_EOS
)
return EosSignedTx(signature[0], signature[1:33], signature[33:])
async def _init(ctx, sha, msg):
writers.write_bytes(sha, msg.chain_id)
writers.write_header(sha, msg.header)
writers.write_variant32(sha, 0)
writers.write_variant32(sha, msg.num_actions)
await require_sign_tx(ctx, msg.num_actions)
async def _actions(ctx, sha, num_actions: int):
for i in range(num_actions):
action = await ctx.call(EosTxActionRequest(), EosTxActionAck)
await process_action(ctx, sha, action)

View File

@ -0,0 +1,168 @@
from trezor.messages import (
EosActionBuyRam,
EosActionBuyRamBytes,
EosActionCommon,
EosActionDelegate,
EosActionDeleteAuth,
EosActionLinkAuth,
EosActionNewAccount,
EosActionRefund,
EosActionSellRam,
EosActionTransfer,
EosActionUndelegate,
EosActionUpdateAuth,
EosActionVoteProducer,
EosAsset,
EosAuthorization,
EosTxHeader,
)
from trezor.utils import HashWriter
from apps.common.writers import (
write_bytes,
write_uint8,
write_uint16_le,
write_uint32_le,
write_uint64_le,
)
def write_auth(w: bytearray, auth: EosAuthorization) -> int:
write_uint32_le(w, auth.threshold)
write_variant32(w, len(auth.keys))
for key in auth.keys:
write_variant32(w, key.type)
write_bytes(w, key.key)
write_uint16_le(w, key.weight)
write_variant32(w, len(auth.accounts))
for account in auth.accounts:
write_uint64_le(w, account.account.actor)
write_uint64_le(w, account.account.permission)
write_uint16_le(w, account.weight)
write_variant32(w, len(auth.waits))
for wait in auth.waits:
write_uint32_le(w, wait.wait_sec)
write_uint16_le(w, wait.weight)
def write_header(hasher: HashWriter, header: EosTxHeader):
write_uint32_le(hasher, header.expiration)
write_uint16_le(hasher, header.ref_block_num)
write_uint32_le(hasher, header.ref_block_prefix)
write_variant32(hasher, header.max_net_usage_words)
write_uint8(hasher, header.max_cpu_usage_ms)
write_variant32(hasher, header.delay_sec)
def write_action_transfer(w: bytearray, msg: EosActionTransfer):
write_uint64_le(w, msg.sender)
write_uint64_le(w, msg.receiver)
write_asset(w, msg.quantity)
write_variant32(w, len(msg.memo))
write_bytes(w, msg.memo)
def write_action_buyram(w: bytearray, msg: EosActionBuyRam):
write_uint64_le(w, msg.payer)
write_uint64_le(w, msg.receiver)
write_asset(w, msg.quantity)
def write_action_buyrambytes(w: bytearray, msg: EosActionBuyRamBytes):
write_uint64_le(w, msg.payer)
write_uint64_le(w, msg.receiver)
write_uint32_le(w, msg.bytes)
def write_action_sellram(w: bytearray, msg: EosActionSellRam):
write_uint64_le(w, msg.account)
write_uint64_le(w, msg.bytes)
def write_action_delegate(w: bytearray, msg: EosActionDelegate):
write_uint64_le(w, msg.sender)
write_uint64_le(w, msg.receiver)
write_asset(w, msg.net_quantity)
write_asset(w, msg.cpu_quantity)
write_uint8(w, 1 if msg.transfer else 0)
def write_action_undelegate(w: bytearray, msg: EosActionUndelegate):
write_uint64_le(w, msg.sender)
write_uint64_le(w, msg.receiver)
write_asset(w, msg.net_quantity)
write_asset(w, msg.cpu_quantity)
def write_action_refund(w: bytearray, msg: EosActionRefund):
write_uint64_le(w, msg.owner)
def write_action_voteproducer(w: bytearray, msg: EosActionVoteProducer):
write_uint64_le(w, msg.voter)
write_uint64_le(w, msg.proxy)
write_variant32(w, len(msg.producers))
for producer in msg.producers:
write_uint64_le(w, producer)
def write_action_updateauth(w: bytearray, msg: EosActionUpdateAuth):
write_uint64_le(w, msg.account)
write_uint64_le(w, msg.permission)
write_uint64_le(w, msg.parent)
write_auth(w, msg.auth)
def write_action_deleteauth(w: bytearray, msg: EosActionDeleteAuth):
write_uint64_le(w, msg.account)
write_uint64_le(w, msg.permission)
def write_action_linkauth(w: bytearray, msg: EosActionLinkAuth):
write_uint64_le(w, msg.account)
write_uint64_le(w, msg.code)
write_uint64_le(w, msg.type)
write_uint64_le(w, msg.requirement)
def write_action_unlinkauth(w: bytearray, msg: EosActionLinkAuth):
write_uint64_le(w, msg.account)
write_uint64_le(w, msg.code)
write_uint64_le(w, msg.type)
def write_action_newaccount(w: bytearray, msg: EosActionNewAccount):
write_uint64_le(w, msg.creator)
write_uint64_le(w, msg.name)
write_auth(w, msg.owner)
write_auth(w, msg.active)
def write_action_common(hasher: HashWriter, msg: EosActionCommon):
write_uint64_le(hasher, msg.account)
write_uint64_le(hasher, msg.name)
write_variant32(hasher, len(msg.authorization))
for authorization in msg.authorization:
write_uint64_le(hasher, authorization.actor)
write_uint64_le(hasher, authorization.permission)
def write_asset(w: bytearray, asset: EosAsset) -> int:
write_uint64_le(w, asset.amount)
write_uint64_le(w, asset.symbol)
def write_variant32(w: bytearray, value: int) -> int:
variant = bytearray()
while True:
b = value & 0x7F
value >>= 7
b |= (value > 0) << 7
variant.append(b)
if value == 0:
break
write_bytes(w, bytes(variant))

View File

@ -20,6 +20,7 @@ import apps.stellar
import apps.ripple
import apps.cardano
import apps.tezos
import apps.eos
if __debug__:
import apps.debug
@ -38,6 +39,7 @@ apps.stellar.boot()
apps.ripple.boot()
apps.cardano.boot()
apps.tezos.boot()
apps.eos.boot()
if __debug__:
apps.debug.boot()
else:

View File

@ -0,0 +1,26 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAsset import EosAsset
class EosActionBuyRam(p.MessageType):
def __init__(
self,
payer: int = None,
receiver: int = None,
quantity: EosAsset = None,
) -> None:
self.payer = payer
self.receiver = receiver
self.quantity = quantity
@classmethod
def get_fields(cls):
return {
1: ('payer', p.UVarintType, 0),
2: ('receiver', p.UVarintType, 0),
3: ('quantity', EosAsset, 0),
}

View File

@ -0,0 +1,24 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionBuyRamBytes(p.MessageType):
def __init__(
self,
payer: int = None,
receiver: int = None,
bytes: int = None,
) -> None:
self.payer = payer
self.receiver = receiver
self.bytes = bytes
@classmethod
def get_fields(cls):
return {
1: ('payer', p.UVarintType, 0),
2: ('receiver', p.UVarintType, 0),
3: ('bytes', p.UVarintType, 0),
}

View File

@ -0,0 +1,32 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosPermissionLevel import EosPermissionLevel
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosActionCommon(p.MessageType):
def __init__(
self,
account: int = None,
name: int = None,
authorization: List[EosPermissionLevel] = None,
) -> None:
self.account = account
self.name = name
self.authorization = authorization if authorization is not None else []
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('name', p.UVarintType, 0),
3: ('authorization', EosPermissionLevel, p.FLAG_REPEATED),
}

View File

@ -0,0 +1,32 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAsset import EosAsset
class EosActionDelegate(p.MessageType):
def __init__(
self,
sender: int = None,
receiver: int = None,
net_quantity: EosAsset = None,
cpu_quantity: EosAsset = None,
transfer: bool = None,
) -> None:
self.sender = sender
self.receiver = receiver
self.net_quantity = net_quantity
self.cpu_quantity = cpu_quantity
self.transfer = transfer
@classmethod
def get_fields(cls):
return {
1: ('sender', p.UVarintType, 0),
2: ('receiver', p.UVarintType, 0),
3: ('net_quantity', EosAsset, 0),
4: ('cpu_quantity', EosAsset, 0),
5: ('transfer', p.BoolType, 0),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionDeleteAuth(p.MessageType):
def __init__(
self,
account: int = None,
permission: int = None,
) -> None:
self.account = account
self.permission = permission
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('permission', p.UVarintType, 0),
}

View File

@ -0,0 +1,27 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionLinkAuth(p.MessageType):
def __init__(
self,
account: int = None,
code: int = None,
type: int = None,
requirement: int = None,
) -> None:
self.account = account
self.code = code
self.type = type
self.requirement = requirement
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('code', p.UVarintType, 0),
3: ('type', p.UVarintType, 0),
4: ('requirement', p.UVarintType, 0),
}

View File

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAuthorization import EosAuthorization
class EosActionNewAccount(p.MessageType):
def __init__(
self,
creator: int = None,
name: int = None,
owner: EosAuthorization = None,
active: EosAuthorization = None,
) -> None:
self.creator = creator
self.name = name
self.owner = owner
self.active = active
@classmethod
def get_fields(cls):
return {
1: ('creator', p.UVarintType, 0),
2: ('name', p.UVarintType, 0),
3: ('owner', EosAuthorization, 0),
4: ('active', EosAuthorization, 0),
}

View File

@ -0,0 +1,18 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionRefund(p.MessageType):
def __init__(
self,
owner: int = None,
) -> None:
self.owner = owner
@classmethod
def get_fields(cls):
return {
1: ('owner', p.UVarintType, 0),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionSellRam(p.MessageType):
def __init__(
self,
account: int = None,
bytes: int = None,
) -> None:
self.account = account
self.bytes = bytes
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('bytes', p.UVarintType, 0),
}

View File

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAsset import EosAsset
class EosActionTransfer(p.MessageType):
def __init__(
self,
sender: int = None,
receiver: int = None,
quantity: EosAsset = None,
memo: str = None,
) -> None:
self.sender = sender
self.receiver = receiver
self.quantity = quantity
self.memo = memo
@classmethod
def get_fields(cls):
return {
1: ('sender', p.UVarintType, 0),
2: ('receiver', p.UVarintType, 0),
3: ('quantity', EosAsset, 0),
4: ('memo', p.UnicodeType, 0),
}

View File

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAsset import EosAsset
class EosActionUndelegate(p.MessageType):
def __init__(
self,
sender: int = None,
receiver: int = None,
net_quantity: EosAsset = None,
cpu_quantity: EosAsset = None,
) -> None:
self.sender = sender
self.receiver = receiver
self.net_quantity = net_quantity
self.cpu_quantity = cpu_quantity
@classmethod
def get_fields(cls):
return {
1: ('sender', p.UVarintType, 0),
2: ('receiver', p.UVarintType, 0),
3: ('net_quantity', EosAsset, 0),
4: ('cpu_quantity', EosAsset, 0),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionUnknown(p.MessageType):
def __init__(
self,
data_size: int = None,
data_chunk: bytes = None,
) -> None:
self.data_size = data_size
self.data_chunk = data_chunk
@classmethod
def get_fields(cls):
return {
1: ('data_size', p.UVarintType, 0),
2: ('data_chunk', p.BytesType, 0),
}

View File

@ -0,0 +1,24 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosActionUnlinkAuth(p.MessageType):
def __init__(
self,
account: int = None,
code: int = None,
type: int = None,
) -> None:
self.account = account
self.code = code
self.type = type
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('code', p.UVarintType, 0),
3: ('type', p.UVarintType, 0),
}

View File

@ -0,0 +1,29 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAuthorization import EosAuthorization
class EosActionUpdateAuth(p.MessageType):
def __init__(
self,
account: int = None,
permission: int = None,
parent: int = None,
auth: EosAuthorization = None,
) -> None:
self.account = account
self.permission = permission
self.parent = parent
self.auth = auth
@classmethod
def get_fields(cls):
return {
1: ('account', p.UVarintType, 0),
2: ('permission', p.UVarintType, 0),
3: ('parent', p.UVarintType, 0),
4: ('auth', EosAuthorization, 0),
}

View File

@ -0,0 +1,30 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosActionVoteProducer(p.MessageType):
def __init__(
self,
voter: int = None,
proxy: int = None,
producers: List[int] = None,
) -> None:
self.voter = voter
self.proxy = proxy
self.producers = producers if producers is not None else []
@classmethod
def get_fields(cls):
return {
1: ('voter', p.UVarintType, 0),
2: ('proxy', p.UVarintType, 0),
3: ('producers', p.UVarintType, p.FLAG_REPEATED),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosAsset(p.MessageType):
def __init__(
self,
amount: int = None,
symbol: int = None,
) -> None:
self.amount = amount
self.symbol = symbol
@classmethod
def get_fields(cls):
return {
1: ('amount', p.SVarintType, 0),
2: ('symbol', p.UVarintType, 0),
}

View File

@ -0,0 +1,37 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosAuthorizationAccount import EosAuthorizationAccount
from .EosAuthorizationKey import EosAuthorizationKey
from .EosAuthorizationWait import EosAuthorizationWait
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosAuthorization(p.MessageType):
def __init__(
self,
threshold: int = None,
keys: List[EosAuthorizationKey] = None,
accounts: List[EosAuthorizationAccount] = None,
waits: List[EosAuthorizationWait] = None,
) -> None:
self.threshold = threshold
self.keys = keys if keys is not None else []
self.accounts = accounts if accounts is not None else []
self.waits = waits if waits is not None else []
@classmethod
def get_fields(cls):
return {
1: ('threshold', p.UVarintType, 0),
2: ('keys', EosAuthorizationKey, p.FLAG_REPEATED),
3: ('accounts', EosAuthorizationAccount, p.FLAG_REPEATED),
4: ('waits', EosAuthorizationWait, p.FLAG_REPEATED),
}

View File

@ -0,0 +1,23 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosPermissionLevel import EosPermissionLevel
class EosAuthorizationAccount(p.MessageType):
def __init__(
self,
account: EosPermissionLevel = None,
weight: int = None,
) -> None:
self.account = account
self.weight = weight
@classmethod
def get_fields(cls):
return {
1: ('account', EosPermissionLevel, 0),
2: ('weight', p.UVarintType, 0),
}

View File

@ -0,0 +1,33 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosAuthorizationKey(p.MessageType):
def __init__(
self,
type: int = None,
key: bytes = None,
address_n: List[int] = None,
weight: int = None,
) -> None:
self.type = type
self.key = key
self.address_n = address_n if address_n is not None else []
self.weight = weight
@classmethod
def get_fields(cls):
return {
1: ('type', p.UVarintType, 0),
2: ('key', p.BytesType, 0),
3: ('address_n', p.UVarintType, p.FLAG_REPEATED),
4: ('weight', p.UVarintType, 0),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosAuthorizationWait(p.MessageType):
def __init__(
self,
wait_sec: int = None,
weight: int = None,
) -> None:
self.wait_sec = wait_sec
self.weight = weight
@classmethod
def get_fields(cls):
return {
1: ('wait_sec', p.UVarintType, 0),
2: ('weight', p.UVarintType, 0),
}

View File

@ -0,0 +1,28 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosGetPublicKey(p.MessageType):
MESSAGE_WIRE_TYPE = 600
def __init__(
self,
address_n: List[int] = None,
show_display: bool = None,
) -> None:
self.address_n = address_n if address_n is not None else []
self.show_display = show_display
@classmethod
def get_fields(cls):
return {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('show_display', p.BoolType, 0),
}

View File

@ -0,0 +1,21 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosPermissionLevel(p.MessageType):
def __init__(
self,
actor: int = None,
permission: int = None,
) -> None:
self.actor = actor
self.permission = permission
@classmethod
def get_fields(cls):
return {
1: ('actor', p.UVarintType, 0),
2: ('permission', p.UVarintType, 0),
}

View File

@ -0,0 +1,22 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosPublicKey(p.MessageType):
MESSAGE_WIRE_TYPE = 601
def __init__(
self,
wif_public_key: str = None,
raw_public_key: bytes = None,
) -> None:
self.wif_public_key = wif_public_key
self.raw_public_key = raw_public_key
@classmethod
def get_fields(cls):
return {
1: ('wif_public_key', p.UnicodeType, 0),
2: ('raw_public_key', p.BytesType, 0),
}

View File

@ -0,0 +1,36 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosTxHeader import EosTxHeader
if __debug__:
try:
from typing import List
except ImportError:
List = None # type: ignore
class EosSignTx(p.MessageType):
MESSAGE_WIRE_TYPE = 602
def __init__(
self,
address_n: List[int] = None,
chain_id: bytes = None,
header: EosTxHeader = None,
num_actions: int = None,
) -> None:
self.address_n = address_n if address_n is not None else []
self.chain_id = chain_id
self.header = header
self.num_actions = num_actions
@classmethod
def get_fields(cls):
return {
1: ('address_n', p.UVarintType, p.FLAG_REPEATED),
2: ('chain_id', p.BytesType, 0),
3: ('header', EosTxHeader, 0),
4: ('num_actions', p.UVarintType, 0),
}

View File

@ -0,0 +1,25 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosSignedTx(p.MessageType):
MESSAGE_WIRE_TYPE = 605
def __init__(
self,
signature_v: int = None,
signature_r: bytes = None,
signature_s: bytes = None,
) -> None:
self.signature_v = signature_v
self.signature_r = signature_r
self.signature_s = signature_s
@classmethod
def get_fields(cls):
return {
1: ('signature_v', p.UVarintType, 0),
2: ('signature_r', p.BytesType, 0),
3: ('signature_s', p.BytesType, 0),
}

View File

@ -0,0 +1,77 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
from .EosActionBuyRam import EosActionBuyRam
from .EosActionBuyRamBytes import EosActionBuyRamBytes
from .EosActionCommon import EosActionCommon
from .EosActionDelegate import EosActionDelegate
from .EosActionDeleteAuth import EosActionDeleteAuth
from .EosActionLinkAuth import EosActionLinkAuth
from .EosActionNewAccount import EosActionNewAccount
from .EosActionRefund import EosActionRefund
from .EosActionSellRam import EosActionSellRam
from .EosActionTransfer import EosActionTransfer
from .EosActionUndelegate import EosActionUndelegate
from .EosActionUnknown import EosActionUnknown
from .EosActionUnlinkAuth import EosActionUnlinkAuth
from .EosActionUpdateAuth import EosActionUpdateAuth
from .EosActionVoteProducer import EosActionVoteProducer
class EosTxActionAck(p.MessageType):
MESSAGE_WIRE_TYPE = 604
def __init__(
self,
common: EosActionCommon = None,
transfer: EosActionTransfer = None,
delegate: EosActionDelegate = None,
undelegate: EosActionUndelegate = None,
refund: EosActionRefund = None,
buy_ram: EosActionBuyRam = None,
buy_ram_bytes: EosActionBuyRamBytes = None,
sell_ram: EosActionSellRam = None,
vote_producer: EosActionVoteProducer = None,
update_auth: EosActionUpdateAuth = None,
delete_auth: EosActionDeleteAuth = None,
link_auth: EosActionLinkAuth = None,
unlink_auth: EosActionUnlinkAuth = None,
new_account: EosActionNewAccount = None,
unknown: EosActionUnknown = None,
) -> None:
self.common = common
self.transfer = transfer
self.delegate = delegate
self.undelegate = undelegate
self.refund = refund
self.buy_ram = buy_ram
self.buy_ram_bytes = buy_ram_bytes
self.sell_ram = sell_ram
self.vote_producer = vote_producer
self.update_auth = update_auth
self.delete_auth = delete_auth
self.link_auth = link_auth
self.unlink_auth = unlink_auth
self.new_account = new_account
self.unknown = unknown
@classmethod
def get_fields(cls):
return {
1: ('common', EosActionCommon, 0),
2: ('transfer', EosActionTransfer, 0),
3: ('delegate', EosActionDelegate, 0),
4: ('undelegate', EosActionUndelegate, 0),
5: ('refund', EosActionRefund, 0),
6: ('buy_ram', EosActionBuyRam, 0),
7: ('buy_ram_bytes', EosActionBuyRamBytes, 0),
8: ('sell_ram', EosActionSellRam, 0),
9: ('vote_producer', EosActionVoteProducer, 0),
10: ('update_auth', EosActionUpdateAuth, 0),
11: ('delete_auth', EosActionDeleteAuth, 0),
12: ('link_auth', EosActionLinkAuth, 0),
13: ('unlink_auth', EosActionUnlinkAuth, 0),
14: ('new_account', EosActionNewAccount, 0),
15: ('unknown', EosActionUnknown, 0),
}

View File

@ -0,0 +1,19 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosTxActionRequest(p.MessageType):
MESSAGE_WIRE_TYPE = 603
def __init__(
self,
data_size: int = None,
) -> None:
self.data_size = data_size
@classmethod
def get_fields(cls):
return {
1: ('data_size', p.UVarintType, 0),
}

View File

@ -0,0 +1,33 @@
# Automatically generated by pb2py
# fmt: off
import protobuf as p
class EosTxHeader(p.MessageType):
def __init__(
self,
expiration: int = None,
ref_block_num: int = None,
ref_block_prefix: int = None,
max_net_usage_words: int = None,
max_cpu_usage_ms: int = None,
delay_sec: int = None,
) -> None:
self.expiration = expiration
self.ref_block_num = ref_block_num
self.ref_block_prefix = ref_block_prefix
self.max_net_usage_words = max_net_usage_words
self.max_cpu_usage_ms = max_cpu_usage_ms
self.delay_sec = delay_sec
@classmethod
def get_fields(cls):
return {
1: ('expiration', p.UVarintType, 0), # required
2: ('ref_block_num', p.UVarintType, 0), # required
3: ('ref_block_prefix', p.UVarintType, 0), # required
4: ('max_net_usage_words', p.UVarintType, 0), # required
5: ('max_cpu_usage_ms', p.UVarintType, 0), # required
6: ('delay_sec', p.UVarintType, 0), # required
}

View File

@ -0,0 +1,64 @@
from common import *
from apps.eos.actions import check_action
from trezor.messages.EosTxActionAck import EosTxActionAck
from trezor.messages.EosActionBuyRam import EosActionBuyRam
from trezor.messages.EosActionBuyRamBytes import EosActionBuyRamBytes
from trezor.messages.EosActionDelegate import EosActionDelegate
from trezor.messages.EosActionDeleteAuth import EosActionDeleteAuth
from trezor.messages.EosActionLinkAuth import EosActionLinkAuth
from trezor.messages.EosActionNewAccount import EosActionNewAccount
from trezor.messages.EosActionRefund import EosActionRefund
from trezor.messages.EosActionSellRam import EosActionSellRam
from trezor.messages.EosActionTransfer import EosActionTransfer
from trezor.messages.EosActionUndelegate import EosActionUndelegate
from trezor.messages.EosActionUnlinkAuth import EosActionUnlinkAuth
from trezor.messages.EosActionUpdateAuth import EosActionUpdateAuth
from trezor.messages.EosActionVoteProducer import EosActionVoteProducer
class TestEosActions(unittest.TestCase):
def test_check_action(self):
# return True
self.assertEqual(check_action(EosTxActionAck(buy_ram=EosActionBuyRam()), 'buyram', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(buy_ram_bytes=EosActionBuyRamBytes()), 'buyrambytes', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(sell_ram=EosActionSellRam()), 'sellram', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(delegate=EosActionDelegate()), 'delegatebw', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(undelegate=EosActionDeleteAuth()), 'undelegatebw', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(refund=EosActionRefund()), 'refund', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(vote_producer=EosActionVoteProducer()), 'voteproducer', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(update_auth=EosActionUpdateAuth()), 'updateauth', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(delete_auth=EosActionDeleteAuth()), 'deleteauth', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(link_auth=EosActionLinkAuth()), 'linkauth', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(unlink_auth=EosActionUnlinkAuth()), 'unlinkauth', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(new_account=EosActionNewAccount()), 'newaccount', 'eosio'), True)
self.assertEqual(check_action(EosTxActionAck(transfer=EosActionTransfer()), 'transfer', 'not_eosio'), True)
self.assertEqual(check_action(EosTxActionAck(unknown=[]), 'unknown', 'not_eosio'), True)
self.assertEqual(check_action(EosTxActionAck(unknown=[]), 'buyram', 'buygoods'), True)
# returns False
self.assertEqual(check_action(EosTxActionAck(buy_ram=EosActionBuyRam()), 'buyram', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(), 'buyram', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(buy_ram_bytes=EosActionBuyRamBytes()), 'buyrambytes', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(sell_ram=EosActionSellRam()), 'sellram', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(delegate=EosActionDelegate()), 'delegatebw', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(undelegate=EosActionDeleteAuth()), 'undelegatebw', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(refund=EosActionRefund()), 'refund', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(), 'refund', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(vote_producer=EosActionVoteProducer()), 'voteproducer', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(update_auth=EosActionUpdateAuth()), 'updateauth', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(delete_auth=EosActionDeleteAuth()), 'deleteauth', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(link_auth=EosActionLinkAuth()), 'linkauth', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(unlink_auth=EosActionUnlinkAuth()), 'unlinkauth', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(), 'unlinkauth', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(new_account=EosActionNewAccount()), 'newaccount', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(transfer=EosActionTransfer()), 'transfer', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(), 'unknown', 'not_eosio'), False)
self.assertEqual(check_action(EosTxActionAck(buy_ram=EosActionBuyRam()), 'test', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(unknown=[]), 'buyram', 'eosio'), False)
self.assertEqual(check_action(EosTxActionAck(unknown=[]), 'transfer', 'loveme'), False)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,66 @@
from common import *
from apps.eos import helpers
from trezor.messages.EosAsset import EosAsset
class TestEosConversions(unittest.TestCase):
def test_eos_name_to_string(self):
names_in = [
10639447606881920736,
614251623682315968,
614251535012020768,
7754926748989239168,
14895601873759291472,
595056260442243600,
]
names_out = [
'miniminimini',
'12345abcdefg',
'123451234512',
'hijklmnopqrs',
'tuvwxyz12345',
'111111111111',
]
for i, o in zip(names_in, names_out):
self.assertEqual(helpers.eos_name_to_string(i), o)
def test_eos_asset_to_string(self):
asset_in = [
EosAsset(
amount=10000,
symbol=1397703940,
),
EosAsset(
amount=200000,
symbol=1397703940,
),
EosAsset(
amount=255000,
symbol=1397703940,
),
EosAsset(
amount=999999,
symbol=1397703939,
),
EosAsset(
amount=1,
symbol=1397703940,
),
EosAsset(
amount=999,
symbol=1397703939,
),
]
asset_out = [
'1.0000 EOS',
'20.0000 EOS',
'25.5000 EOS',
'999.999 EOS',
'0.0001 EOS',
'0.999 EOS',
]
for i, o in zip(asset_in, asset_out):
self.assertEqual(helpers.eos_asset_to_string(i), o)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,72 @@
from common import *
from apps.eos.get_public_key import _get_public_key, _public_key_to_wif
from trezor.crypto import bip32, bip39
from ubinascii import hexlify, unhexlify
from apps.common.paths import HARDENED
from apps.eos.helpers import validate_full_path
class TestEosGetPublicKey(unittest.TestCase):
def test_get_public_key_scheme(self):
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
seed = bip39.seed(mnemonic, '')
derivation_paths = [
[0x80000000 | 44, 0x80000000 | 194, 0x80000000, 0, 0],
[0x80000000 | 44, 0x80000000 | 194, 0x80000000, 0, 1],
[0x80000000 | 44, 0x80000000 | 194],
[0x80000000 | 44, 0x80000000 | 194, 0x80000000, 0, 0x80000000],
]
public_keys = [
b'0315c358024ce46767102578947584c4342a6982b922d454f63588effa34597197',
b'029622eff7248c4d298fe28f2df19ee0d5f7674f678844e05c31d1a5632412869e',
b'02625f33c10399703e95e41bd5054beef3ab893dcc7df2bb9bdcee48359b29069d',
b'037c9b7d24d42589941cca3f4debc75b37c0e7b881e6eb00d2e674958debe3bbc3',
]
wif_keys = [
'EOS6zpSNY1YoLxNt2VsvJjoDfBueU6xC1M1ERJw1UoekL1NHn8KNA',
'EOS62cPUiWnLqbUjiBMxbEU4pm4Hp5X3RGk4KMTadvZNygjX72yHW',
'EOS5dp8aCFoFwrKo6KuUfos1hwMfZGkiZUbaF2CyuD4chyBEN2wQK',
'EOS7n7TXwR4Y3DtPt2ji6akhQi5uw4SruuPArvoNJso84vhwPQt1G',
]
for index, path in enumerate(derivation_paths):
node = bip32.from_seed(seed, 'secp256k1')
node.derive_path(path)
wif, public_key = _get_public_key(node)
self.assertEqual(hexlify(public_key), public_keys[index])
self.assertEqual(wif, wif_keys[index])
self.assertEqual(_public_key_to_wif(public_key), wif_keys[index])
def test_paths(self):
# 44'/194'/a'/0/0 is correct
incorrect_paths = [
[44 | HARDENED],
[44 | HARDENED, 194 | HARDENED],
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 0, 0, 0],
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 0 | HARDENED],
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED],
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 1, 0],
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 0, 1],
[44 | HARDENED, 160 | HARDENED, 0 | HARDENED, 0, 0],
[44 | HARDENED, 199 | HARDENED, 0 | HARDENED, 0, 9999],
]
correct_paths = [
[44 | HARDENED, 194 | HARDENED, 0 | HARDENED, 0, 0],
[44 | HARDENED, 194 | HARDENED, 9 | HARDENED, 0, 0],
[44 | HARDENED, 194 | HARDENED, 9999 | HARDENED, 0, 0],
]
for path in incorrect_paths:
self.assertFalse(validate_full_path(path))
for path in correct_paths:
self.assertTrue(validate_full_path(path))
if __name__ == '__main__':
unittest.main()

View File

@ -12,6 +12,7 @@ rm -f ../src/trezor/messages/[A-Z]*.py
../vendor/trezor-common/protob/messages-common.proto \
../vendor/trezor-common/protob/messages-crypto.proto \
../vendor/trezor-common/protob/messages-debug.proto \
../vendor/trezor-common/protob/messages-eos.proto \
../vendor/trezor-common/protob/messages-ethereum.proto \
../vendor/trezor-common/protob/messages-lisk.proto \
../vendor/trezor-common/protob/messages-management.proto \

View File

@ -32,6 +32,8 @@ Use the following command to see all options:
disable-passphrase Disable passphrase.
enable-passphrase Enable passphrase.
encrypt-keyvalue Encrypt value by given key and path.
eos_get_public_key Get EOS public key in base58 encoding.
eos_sign_transaction Sign EOS transaction...
ethereum-get-address Get Ethereum address in hex encoding.
ethereum-sign-message Sign message with Ethereum address.
ethereum-sign-tx Sign (and optionally publish) Ethereum transaction.

View File

@ -37,6 +37,7 @@ from trezorlib import (
cosi,
debuglink,
device,
eos,
ethereum,
exceptions,
firmware,
@ -1414,6 +1415,48 @@ def ethereum_sign_tx(
return "Signed raw transaction:\n%s" % tx_hex
#
# EOS functions
#
@cli.command(help="Get Eos public key in base58 encoding.")
@click.option(
"-n", "--address", required=True, help="BIP-32 path, e.g. m/44'/194'/0'/0/0"
)
@click.option("-d", "--show-display", is_flag=True)
@click.pass_obj
def eos_get_public_key(connect, address, show_display):
client = connect()
address_n = tools.parse_path(address)
res = eos.get_public_key(client, address_n, show_display)
return "WIF: {}\nRaw: {}".format(res.wif_public_key, res.raw_public_key.hex())
@cli.command(help="Init sign (and optionally publish) EOS transaction. ")
@click.option(
"-n",
"--address",
required=True,
help="BIP-32 path to source address, e.g., m/44'/194'/0'/0/0",
)
@click.option(
"-f",
"--file",
type=click.File("r"),
required=True,
help="Transaction in JSON format",
)
@click.pass_obj
def eos_sign_transaction(connect, address, file):
client = connect()
tx_json = json.load(file)
address_n = tools.parse_path(address)
return eos.sign_tx(client, address_n, tx_json["transaction"], tx_json["chain_id"])
#
# ADA functions
#

332
python/trezorlib/eos.py Normal file
View File

@ -0,0 +1,332 @@
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

View File

@ -0,0 +1,40 @@
import pytest
from trezorlib.eos import get_public_key
from trezorlib.tools import parse_path
from .common import TrezorTest
@pytest.mark.skip_t1
@pytest.mark.eos
class TestMsgEosGetpublickey(TrezorTest):
def test_eos_get_public_key(self):
self.setup_mnemonic_nopin_nopassphrase()
public_key = get_public_key(self.client, parse_path("m/44'/194'/0'/0/0"))
assert (
public_key.wif_public_key
== "EOS4u6Sfnzj4Sh2pEQnkXyZQJqH3PkKjGByDCbsqqmyq6PttM9KyB"
)
assert (
public_key.raw_public_key.hex()
== "02015fabe197c955036bab25f4e7c16558f9f672f9f625314ab1ec8f64f7b1198e"
)
public_key = get_public_key(self.client, parse_path("m/44'/194'/0'/0/1"))
assert (
public_key.wif_public_key
== "EOS5d1VP15RKxT4dSakWu2TFuEgnmaGC2ckfSvQwND7pZC1tXkfLP"
)
assert (
public_key.raw_public_key.hex()
== "02608bc2c431521dee0b9d5f2fe34053e15fc3b20d2895e0abda857b9ed8e77a78"
)
public_key = get_public_key(self.client, parse_path("m/44'/194'/1'/0/0"))
assert (
public_key.wif_public_key
== "EOS7UuNeTf13nfcG85rDB7AHGugZi4C4wJ4ft12QRotqNfxdV2NvP"
)
assert (
public_key.raw_public_key.hex()
== "035588a197bd5a7356e8a702361b2d535c6372f843874bed6617cd1afe1dfcb502"
)

View File

@ -0,0 +1,860 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2018 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import time
import pytest
from trezorlib import eos
from trezorlib.messages import EosSignedTx
from trezorlib.tools import parse_path
from .common import TrezorTest
CHAIN_ID = "cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"
ADDRESS_N = parse_path("m/44'/194'/0'/0/0")
@pytest.mark.skip_t1
@pytest.mark.eos
class TestMsgEosSignTx(TrezorTest):
def input_flow(self, pages):
# confirm number of actions
yield
self.client.debug.press_yes()
# swipe through pages
yield
for _ in range(pages - 1):
self.client.debug.swipe_down()
time.sleep(1)
# confirm last page
self.client.debug.press_yes()
def test_eos_signtx_transfer_token(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio.token",
"name": "transfer",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"from": "miniminimini",
"to": "maximaximaxi",
"quantity": "1.0000 EOS",
"memo": "testtest",
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=3))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "0a9a0f467697010b743ffd02eae6698464c8b5c84b696245397287c225f85e01"
)
assert (
resp.signature_s.hex()
== "3ec6a0175e5209be6789587a9d6b5f61593b841a751112faa05d9efdd9239d40"
)
assert resp.signature_v == 31
def test_eos_signtx_buyram(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "buyram",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"payer": "miniminimini",
"receiver": "miniminimini",
"quant": "1000000000.0000 EOS",
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "480bdc505ba196d445d92ea12bda9d39f986d01620efcffe98bcf645ddcbb4ec"
)
assert (
resp.signature_s.hex()
== "35c8e2105f0228c9e1e511682ae79eac1b7b90bc84c1a0dae13245b7f0f96abf"
)
assert resp.signature_v == 31
def test_eos_signtx_buyrambytes(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "buyrambytes",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"payer": "miniminimini",
"receiver": "miniminimini",
"bytes": 1023,
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "52267ee5f3ff73939af5ccdaa3406e0783deaf76accf5ce4ceb9714cdbdf7d6b"
)
assert (
resp.signature_s.hex()
== "53aa9a9ecf044396441a559b51d3b97e239321c895823aad6888b0de2063a078"
)
assert resp.signature_v == 31
def test_eos_signtx_sellram(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "sellram",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {"account": "miniminimini", "bytes": 1024},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "04c0fdf1d2e0ea21af292173eacc2c7db90f7764abe69b79a8c2b24201af27c4"
)
assert (
resp.signature_s.hex()
== "7bb29f12eaaabbebdb5190d30367012a80128138b5024b30e93e3afb3d24734e"
)
assert resp.signature_v == 31
def test_eos_signtx_delegate(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "delegatebw",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"sender": "miniminimini",
"receiver": "maximaximaxi",
"stake_net_quantity": "1.0000 EOS",
"stake_cpu_quantity": "1.0000 EOS",
"transfer": True,
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=3))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "03b4ccb74b7ad54f28fdeda244facb3038cf70424fd6aa4b171a3bb02a591504"
)
assert (
resp.signature_s.hex()
== "4e24e08d1789421e17ba47e0e4635a3721400a795e40a8896dc5e5af4a95343d"
)
assert resp.signature_v == 31
def test_eos_signtx_undelegate(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "undelegatebw",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"sender": "miniminimini",
"receiver": "maximaximaxi",
"unstake_net_quantity": "1.0000 EOS",
"unstake_cpu_quantity": "1.0000 EOS",
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "3f39722a88f12395f3cfcdbe218c185f02295ec07a5da8f4b953d5ec3c9ec36b"
)
assert (
resp.signature_s.hex()
== "7acbae47d60cd538ca28fcc8f3dae8f03b3812e7719dd4e9c069a66dbac5ebf3"
)
assert resp.signature_v == 31
def test_eos_signtx_refund(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "refund",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {"owner": "miniminimini"},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "113c4867f77c371ff4701beb794ff0a0a6a1137a0115d0f4b5245c391e9f596f"
)
assert (
resp.signature_s.hex()
== "27203aaaeb8cdbc92c0af32f840c385ac6202e3b4e927bda59d397ebef513381"
)
assert resp.signature_v == 31
def test_eos_signtx_linkauth(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "linkauth",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"account": "maximaximaxi",
"code": "eosbet",
"type": "whatever",
"requirement": "active",
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "5c9bf154dc77649ccf5a997441fcd4041e9da79149078df27a1c6268cf237c75"
)
assert (
resp.signature_s.hex()
== "3e432ddcd17feb2997145d11240b0ca4344a01e2d96e9886533bca7ffceb10cd"
)
assert resp.signature_v == 32
def test_eos_signtx_unlinkauth(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "unlinkauth",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"account": "miniminimini",
"code": "eosbet",
"type": "whatever",
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "316c296594fd7a4dd3b615d80c630fda256e9a3460b00d4f16eede1fb2af9574"
)
assert (
resp.signature_s.hex()
== "76d023913b4f323cfa857d144bf78a4d561954bb23c5df9a31649c9503c3a3b7"
)
assert resp.signature_v == 31
def test_eos_signtx_updateauth(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "updateauth",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"account": "miniminimini",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 1,
"keys": [
{
"key": "EOS8Dkj827FpinZBGmhTM28B85H9eXiFH5XzvLoeukCJV5sKfLc6K",
"weight": 1,
},
{
"key": "EOS8Dkj827FpinZBGmhTM28B85H9eXiFH5XzvLoeukCJV5sKfLc6K",
"weight": 2,
},
],
"accounts": [
{
"permission": {
"actor": "miniminimini",
"permission": "active",
},
"weight": 3,
}
],
"waits": [{"wait_sec": 55, "weight": 4}],
},
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=8))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "00f0ca8ffa8208e72df509a3b356e77056b234d4db167b58d485f30cb9c61841"
)
assert (
resp.signature_s.hex()
== "3f6fb40ffa4e1cf6f3bcb0d8fa3873a2b5a05384ca9251159968558688a4e43d"
)
assert resp.signature_v == 31
def test_eos_signtx_deleteauth(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "deleteauth",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {"account": "maximaximaxi", "permission": "active"},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "6fe7d66f8be2fe3de23c48561e8a17113d1a0aabcf0d4160e9bd8af90f5a608f"
)
assert (
resp.signature_s.hex()
== "3cec8db96be2f6aa7bb00302cec6ad3c8655b492f9a2b84b3c61df6bc81f0d83"
)
assert resp.signature_v == 32
def test_eos_signtx_vote(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "voteproducer",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"account": "miniminimini",
"proxy": "",
"producers": [
"argentinaeos",
"bitfinexeos1",
"cryptolions1",
"eos42freedom",
"eosamsterdam",
"eosasia11111",
"eosauthority",
"eosbeijingbp",
"eosbixinboot",
"eoscafeblock",
"eoscanadacom",
"eoscannonchn",
"eoscleanerbp",
"eosdacserver",
"eosfishrocks",
"eosflytomars",
"eoshuobipool",
"eosisgravity",
"eoslaomaocom",
"eosliquideos",
"eosnewyorkio",
"eosriobrazil",
"eosswedenorg",
"eostribeprod",
"helloeoscnbp",
"jedaaaaaaaaa",
"libertyblock",
"starteosiobp",
"teamgreymass",
],
},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=6))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "1a303dcb27d2d17bc9efc89b10c41d9d78f7e3d671e3475bb1115b988f918770"
)
assert (
resp.signature_s.hex()
== "07869385bf3af8cf0a4ee9daf4f8dd122650c7d59da48d6d9ce1e26b59753324"
)
assert resp.signature_v == 31
def test_eos_signtx_vote_proxy(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "voteproducer",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {"account": "miniminimini", "proxy": "", "producers": []},
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "6f511059a910d256ac20483bfedef2ada3b2d04f3261c97c0fce9455ca8b7ef4"
)
assert (
resp.signature_s.hex()
== "58d795deaf5c9b686e5bcaeabee801ad78e6675f051c24972d8c47abd33585f0"
)
assert resp.signature_v == 32
def test_eos_signtx_unknown(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "foocontract",
"name": "baraction",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": "deadbeef",
}
],
"transaction_extensions": [],
}
with self.client:
self.client.set_input_flow(self.input_flow(pages=2))
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "0bcc986299cf4eb1d5e5bc73620972b2b6683cd4230953a6f1725017927fd9ba"
)
assert (
resp.signature_s.hex()
== "488f7830e30eea5c7b4a96156bf7ffb0983c45a96211ca070b9db3bc6ba4db02"
)
assert resp.signature_v == 31
def test_eos_signtx_newaccount(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-07-14T10:43:28",
"ref_block_num": 6439,
"ref_block_prefix": 2995713264,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio",
"name": "newaccount",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"creator": "miniminimini",
"name": "maximaximaxi",
"owner": {
"threshold": 1,
"keys": [
{
"key": "EOS8Dkj827FpinZBGmhTM28B85H9eXiFH5XzvLoeukCJV5sKfLc6K",
"weight": 1,
}
],
"accounts": [],
"waits": [],
},
"active": {
"threshold": 1,
"keys": [
{
"key": "EOS8Dkj827FpinZBGmhTM28B85H9eXiFH5XzvLoeukCJV5sKfLc6K",
"weight": 1,
}
],
"accounts": [],
"waits": [],
},
},
},
{
"account": "eosio",
"name": "buyrambytes",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"payer": "miniminimini",
"receiver": "maximaximaxi",
"bytes": 4096,
},
},
{
"account": "eosio",
"name": "delegatebw",
"authorization": [
{"actor": "miniminimini", "permission": "active"}
],
"data": {
"sender": "miniminimini",
"receiver": "maximaximaxi",
"stake_net_quantity": "1.0000 EOS",
"stake_cpu_quantity": "1.0000 EOS",
"transfer": True,
},
},
],
"transaction_extensions": [],
}
def input_flow():
# confirm number of actions
yield
self.client.debug.press_yes()
# swipe through new account
yield
for _ in range(5):
self.client.debug.swipe_down()
time.sleep(1)
# confirm new account
self.client.debug.press_yes()
# swipe through buyrambytes
yield
self.client.debug.swipe_down()
time.sleep(1)
# confirm buyrambytes
self.client.debug.press_yes()
# swipe through delegatebw
yield
for _ in range(2):
self.client.debug.swipe_down()
time.sleep(1)
# confirm delegatebw
self.client.debug.press_yes()
with self.client:
self.client.set_input_flow(input_flow)
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "6346a807eef0257a34269b034df7470e134261833d0da5fe0bd91aedf5a47f86"
)
assert (
resp.signature_s.hex()
== "676a1fcd0d8faff63ec206c8596de9cb5d35037d05f337afdc22c7b9e0863e77"
)
assert resp.signature_v == 32
def test_eos_signtx_setcontract(self):
self.setup_mnemonic_nopin_nopassphrase()
transaction = {
"expiration": "2018-06-19T13:29:53",
"ref_block_num": 30587,
"ref_block_prefix": 338239089,
"net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "eosio1",
"name": "setcode",
"authorization": [
{"actor": "ednazztokens", "permission": "active"}
],
"data": "00" * 1024,
},
{
"account": "eosio1",
"name": "setabi",
"authorization": [
{"actor": "ednazztokens", "permission": "active"}
],
"data": "00" * 1024,
},
],
"transaction_extensions": [],
"context_free_data": [],
}
def input_flow():
# confirm number of actions
yield
self.client.debug.press_yes()
# swipe through setcode
yield
self.client.debug.swipe_down()
time.sleep(1)
# confirm setcode
self.client.debug.press_yes()
# swipe through setabi
yield
self.client.debug.swipe_down()
time.sleep(1)
# confirm setabi
self.client.debug.press_yes()
with self.client:
self.client.set_input_flow(input_flow)
resp = eos.sign_tx(self.client, ADDRESS_N, transaction, CHAIN_ID)
assert isinstance(resp, EosSignedTx)
assert (
resp.signature_r.hex()
== "674bbe7c8c7b9abf03ab38851cb53411e794afff04737895962643b1ed94b7d1"
)
assert (
resp.signature_s.hex()
== "1e47559db68d435494e832a16cc08ae7a67b533013ab3407f7a89d5e28de98b7"
)
assert resp.signature_v == 32