From 6f4e9af1facfc93b40db23975ef47b5660ae2b4e Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Fri, 15 Mar 2019 10:53:25 +0100 Subject: [PATCH 01/13] Add support for tezos voting (proposal and ballot operations) --- protob/messages-tezos.proto | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/protob/messages-tezos.proto b/protob/messages-tezos.proto index e429ce8b86..1bc37b8a00 100644 --- a/protob/messages-tezos.proto +++ b/protob/messages-tezos.proto @@ -55,6 +55,8 @@ message TezosSignTx { optional TezosTransactionOp transaction = 4; // Tezos transaction operation optional TezosOriginationOp origination = 5; // Tezos origination operation optional TezosDelegationOp delegation = 6; // Tezos delegation operation + optional TezosProposalOp proposal = 7; // Tezos proposal operation + optional TezosBallotOp ballot = 8; // Tezos ballot operation /* * Tezos contract ID */ @@ -120,6 +122,24 @@ message TezosSignTx { optional uint64 storage_limit = 5; optional bytes delegate = 6; } + /** + * Structure representing information for proposal + */ + message TezosProposalOp { + optional bytes source = 1; + optional uint64 period = 2; + optional uint64 bytes_in_next_field = 3; + optional bytes proposals = 4; + } + /** + * Structure representing information for ballot + */ + message TezosBallotOp { + optional bytes source = 1; + optional uint64 period = 2; + optional bytes proposal = 3; + optional bytes ballot = 4; + } } /** From a0d89c0785f04ba7dd8473b803703e0074824e33 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Fri, 15 Mar 2019 19:39:16 +0100 Subject: [PATCH 02/13] Add support for tezos voting operations (proposal and ballot) --- src/apps/tezos/helpers.py | 2 + src/apps/tezos/layout.py | 51 ++++++++++++++++- src/apps/tezos/sign_tx.py | 78 +++++++++++++++++++++++++- src/apps/tezos/writers.py | 24 ++++++++ src/trezor/messages/TezosBallotOp.py | 27 +++++++++ src/trezor/messages/TezosProposalOp.py | 27 +++++++++ src/trezor/messages/TezosSignTx.py | 8 +++ 7 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/apps/tezos/writers.py create mode 100644 src/trezor/messages/TezosBallotOp.py create mode 100644 src/trezor/messages/TezosProposalOp.py diff --git a/src/apps/tezos/helpers.py b/src/apps/tezos/helpers.py index 0050b93fd5..5669cd1c59 100644 --- a/src/apps/tezos/helpers.py +++ b/src/apps/tezos/helpers.py @@ -22,6 +22,8 @@ TEZOS_PREFIX_BYTES = { "edsig": [9, 245, 205, 134, 18], # operation hash "o": [5, 116], + # protocola hash + "P": [2, 170], } diff --git a/src/apps/tezos/layout.py b/src/apps/tezos/layout.py index 4b1528efe2..14d474e318 100644 --- a/src/apps/tezos/layout.py +++ b/src/apps/tezos/layout.py @@ -1,5 +1,9 @@ -from trezor import ui +from micropython import const + +from trezor import ui, wire from trezor.messages import ButtonRequestType +from trezor.ui.confirm import CANCELLED, CONFIRMED, ConfirmDialog +from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.ui.text import Text from trezor.utils import chunks, format_amount @@ -66,6 +70,51 @@ def split_address(address): return chunks(address, 18) +def split_proposal(proposal): + return chunks(proposal, 17) + + def format_tezos_amount(value): formatted_value = format_amount(value, TEZOS_AMOUNT_DIVISIBILITY) return formatted_value + " XTZ" + + +async def require_confirm_proposal(ctx, proposals): + text = Text("Submit proposal", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Proposal:") + text.mono(*split_proposal(proposals[0])) + await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +async def require_confirm_ballot(ctx, proposal, ballot): + text = Text("Submit ballot", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Ballot: {}".format(ballot)) + text.bold("Proposal:") + text.mono(*split_proposal(proposal[0])) + await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +# use, when there are more then one proposals in one operation +async def show_proposals(ctx, proposals): + first_page = const(0) + pages = proposals + + paginator = paginate(show_proposal_page, len(pages), first_page, pages) + return await ctx.wait(paginator) + + +@ui.layout +async def show_proposal_page(page: int, page_count: int, pages: list): + + text = Text("Submit proposals", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Proposal {}: ".format(page + 1)) + text.mono(*split_proposal(pages[page])) + content = Scrollpage(text, page, page_count) + + if page + 1 >= page_count: + confirm = await ConfirmDialog(content) + if confirm == CANCELLED: + raise wire.ActionCancelled("Cancelled") + else: + content.render() + await animate_swipe() diff --git a/src/apps/tezos/sign_tx.py b/src/apps/tezos/sign_tx.py index 5cf0e11d3b..ffd665ed99 100644 --- a/src/apps/tezos/sign_tx.py +++ b/src/apps/tezos/sign_tx.py @@ -1,3 +1,5 @@ +from micropython import const + from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 @@ -5,8 +7,19 @@ from trezor.messages import TezosContractType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths -from apps.common.writers import write_bytes, write_uint8 + +# from apps.common.writers import write_bytes, write_uint8 from apps.tezos import helpers, layout +from apps.tezos.writers import ( + write_bool, + write_bytes, + write_uint8, + write_uint16, + write_uint32, + write_uint64, +) + +PROPOSAL_LENGTH = const(32) async def sign_tx(ctx, msg, keychain): @@ -50,6 +63,20 @@ async def sign_tx(ctx, msg, keychain): ctx, source, msg.delegation.fee ) + elif msg.proposal is not None: + proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) + + # byte count larger than PROPOSAL_LENGTH indicates more than 1 proposal, use paginated screen + if msg.proposal.bytes_in_next_field > PROPOSAL_LENGTH: + await layout.show_proposals(ctx, proposed_protocols) + else: + await layout.require_confirm_proposal(ctx, proposed_protocols) + + elif msg.ballot is not None: + proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) + submitted_ballot = _get_ballot(msg.ballot.ballot) + await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot) + else: raise wire.DataError("Invalid operation") @@ -99,11 +126,34 @@ def _get_address_from_contract(address): raise wire.DataError("Invalid contract type") +def _get_protocol_hash_from_msg(proposals): + # split the proposals + proposal_list = list( + [ + proposals[i : i + PROPOSAL_LENGTH] + for i in range(0, len(proposals), PROPOSAL_LENGTH) + ] + ) + return [ + helpers.base58_encode_check(proposal, prefix="P") for proposal in proposal_list + ] + + +def _get_ballot(encoded_ballot): + encoded_ballot = int(encoded_ballot[0]) + if encoded_ballot == 0: + return "yay" + elif encoded_ballot == 1: + return "nay" + elif encoded_ballot == 2: + return "pass" + + def _get_operation_bytes(w: bytearray, msg): write_bytes(w, msg.branch) # when the account sends first operation in lifetime, - # we need to reveal its publickey + # we need to reveal its public key if msg.reveal is not None: _encode_common(w, msg.reveal, "reveal") write_bytes(w, msg.reveal.public_key) @@ -127,6 +177,10 @@ def _get_operation_bytes(w: bytearray, msg): elif msg.delegation is not None: _encode_common(w, msg.delegation, "delegation") _encode_data_with_bool_prefix(w, msg.delegation.delegate) + elif msg.proposal is not None: + _encode_proposal(w, msg.proposal) + elif msg.ballot is not None: + _encode_ballot(w, msg.ballot) def _encode_common(w: bytearray, operation, str_operation): @@ -169,3 +223,23 @@ def _encode_zarith(w: bytearray, num): break write_uint8(w, 128 | byte) + + +def _encode_proposal(w: bytearray, proposal): + proposal_tag = 5 + + write_uint8(w, proposal_tag) + write_bytes(w, proposal.source) + write_uint32(w, proposal.period) + write_uint32(w, proposal.bytes_in_next_field) + write_bytes(w, proposal.proposals) + + +def _encode_ballot(w: bytearray, ballot): + ballot_tag = 6 + + write_uint8(w, ballot_tag) + write_bytes(w, ballot.source) + write_uint32(w, ballot.period) + write_bytes(w, ballot.proposal) + write_bytes(w, ballot.ballot) diff --git a/src/apps/tezos/writers.py b/src/apps/tezos/writers.py new file mode 100644 index 0000000000..0c10054c82 --- /dev/null +++ b/src/apps/tezos/writers.py @@ -0,0 +1,24 @@ +from apps.common.writers import ( + write_bytes, + write_uint8, + write_uint32_be, + write_uint64_be, +) + +write_uint8 = write_uint8 +write_uint32 = write_uint32_be +write_uint64 = write_uint64_be +write_bytes = write_bytes + + +def write_bool(w: bytearray, boolean: bool): + if boolean: + write_uint8(w, 255) + else: + write_uint8(w, 0) + + +# write uint16 in be +def write_uint16(w: bytearray, n: int): + w.append((n >> 8) & 0xFF) + w.append(n & 0xFF) diff --git a/src/trezor/messages/TezosBallotOp.py b/src/trezor/messages/TezosBallotOp.py new file mode 100644 index 0000000000..260bf2a4b5 --- /dev/null +++ b/src/trezor/messages/TezosBallotOp.py @@ -0,0 +1,27 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + + +class TezosBallotOp(p.MessageType): + + def __init__( + self, + source: bytes = None, + period: int = None, + proposal: bytes = None, + ballot: bytes = None, + ) -> None: + self.source = source + self.period = period + self.proposal = proposal + self.ballot = ballot + + @classmethod + def get_fields(cls): + return { + 1: ('source', p.BytesType, 0), + 2: ('period', p.UVarintType, 0), + 3: ('proposal', p.BytesType, 0), + 4: ('ballot', p.BytesType, 0), + } diff --git a/src/trezor/messages/TezosProposalOp.py b/src/trezor/messages/TezosProposalOp.py new file mode 100644 index 0000000000..94f0dd532e --- /dev/null +++ b/src/trezor/messages/TezosProposalOp.py @@ -0,0 +1,27 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + + +class TezosProposalOp(p.MessageType): + + def __init__( + self, + source: bytes = None, + period: int = None, + bytes_in_next_field: int = None, + proposals: bytes = None, + ) -> None: + self.source = source + self.period = period + self.bytes_in_next_field = bytes_in_next_field + self.proposals = proposals + + @classmethod + def get_fields(cls): + return { + 1: ('source', p.BytesType, 0), + 2: ('period', p.UVarintType, 0), + 3: ('bytes_in_next_field', p.UVarintType, 0), + 4: ('proposals', p.BytesType, 0), + } diff --git a/src/trezor/messages/TezosSignTx.py b/src/trezor/messages/TezosSignTx.py index 8127603780..cbba27e04f 100644 --- a/src/trezor/messages/TezosSignTx.py +++ b/src/trezor/messages/TezosSignTx.py @@ -2,8 +2,10 @@ # fmt: off import protobuf as p +from .TezosBallotOp import TezosBallotOp from .TezosDelegationOp import TezosDelegationOp from .TezosOriginationOp import TezosOriginationOp +from .TezosProposalOp import TezosProposalOp from .TezosRevealOp import TezosRevealOp from .TezosTransactionOp import TezosTransactionOp @@ -25,6 +27,8 @@ class TezosSignTx(p.MessageType): transaction: TezosTransactionOp = None, origination: TezosOriginationOp = None, delegation: TezosDelegationOp = None, + proposal: TezosProposalOp = None, + ballot: TezosBallotOp = None, ) -> None: self.address_n = address_n if address_n is not None else [] self.branch = branch @@ -32,6 +36,8 @@ class TezosSignTx(p.MessageType): self.transaction = transaction self.origination = origination self.delegation = delegation + self.proposal = proposal + self.ballot = ballot @classmethod def get_fields(cls): @@ -42,4 +48,6 @@ class TezosSignTx(p.MessageType): 4: ('transaction', TezosTransactionOp, 0), 5: ('origination', TezosOriginationOp, 0), 6: ('delegation', TezosDelegationOp, 0), + 7: ('proposal', TezosProposalOp, 0), + 8: ('ballot', TezosBallotOp, 0), } From fd95ff05312da4e5196481d8f5dc9437a6285e72 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Tue, 16 Apr 2019 19:20:40 +0200 Subject: [PATCH 03/13] made requested changes --- common/protob/messages-tezos.proto | 15 +- core/src/apps/common/writers.py | 7 + core/src/apps/tezos/helpers.py | 15 +- core/src/apps/tezos/layout.py | 17 +-- core/src/apps/tezos/sign_tx.py | 68 ++++----- core/src/trezor/messages/TezosBallotOp.py | 4 +- core/src/trezor/messages/TezosBallotType.py | 5 + core/src/trezor/messages/TezosProposalOp.py | 15 +- .../device_tests/test_msg_tezos_sign_tx.py | 138 ++++++++++++++++++ 9 files changed, 216 insertions(+), 68 deletions(-) create mode 100644 core/src/trezor/messages/TezosBallotType.py diff --git a/common/protob/messages-tezos.proto b/common/protob/messages-tezos.proto index 1bc37b8a00..d6fbfea65a 100644 --- a/common/protob/messages-tezos.proto +++ b/common/protob/messages-tezos.proto @@ -126,19 +126,24 @@ message TezosSignTx { * Structure representing information for proposal */ message TezosProposalOp { - optional bytes source = 1; + optional bytes source = 1; //Contains only public_key_hash, not to be confused with TezosContractID optional uint64 period = 2; - optional uint64 bytes_in_next_field = 3; - optional bytes proposals = 4; + repeated bytes proposals = 4; } /** * Structure representing information for ballot */ message TezosBallotOp { - optional bytes source = 1; + optional bytes source = 1; //Contains only public_key_hash, not to be confused with TezosContractID optional uint64 period = 2; optional bytes proposal = 3; - optional bytes ballot = 4; + optional TezosBallotType ballot = 4; + + enum TezosBallotType { + Yay = 0; + Nay = 1; + Pass = 2; + } } } diff --git a/core/src/apps/common/writers.py b/core/src/apps/common/writers.py index a4ea21a580..feeb32590e 100644 --- a/core/src/apps/common/writers.py +++ b/core/src/apps/common/writers.py @@ -24,6 +24,13 @@ def write_uint16_le(w: bytearray, n: int) -> int: return 2 +def write_uint16_be(w: bytearray, n: int): + ensure(0 <= n <= 0xFFFF) + w.append((n >> 8) & 0xFF) + w.append(n & 0xFF) + return 2 + + def write_uint32_le(w: bytearray, n: int) -> int: ensure(0 <= n <= 0xFFFFFFFF) w.append(n & 0xFF) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 102b0b6e5a..e93b493881 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -3,6 +3,7 @@ from micropython import const from trezor.crypto import base58 from apps.common import HARDENED +from apps.common.writers import write_uint8 TEZOS_AMOUNT_DIVISIBILITY = const(6) TEZOS_ED25519_ADDRESS_PREFIX = "tz1" @@ -21,7 +22,7 @@ TEZOS_PREFIX_BYTES = { "edsig": [9, 245, 205, 134, 18], # operation hash "o": [5, 116], - # protocola hash + # protocol hash "P": [2, 170], } @@ -45,7 +46,8 @@ def validate_full_path(path: list) -> bool: Validates derivation path to equal 44'/1729'/a', where `a` is an account index from 0 to 1 000 000. """ - if len(path) != 3: + length = len(path) + if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: return False @@ -53,4 +55,13 @@ def validate_full_path(path: list) -> bool: return False if path[2] < HARDENED or path[2] > 1000000 | HARDENED: return False + if length > 3 and (path[3] < HARDENED or path[3] > 1000000 | HARDENED): + return False return True + + +def write_bool(w: bytearray, boolean: bool): + if boolean: + write_uint8(w, 255) + else: + write_uint8(w, 0) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 14d474e318..315875fea7 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -79,18 +79,11 @@ def format_tezos_amount(value): return formatted_value + " XTZ" -async def require_confirm_proposal(ctx, proposals): - text = Text("Submit proposal", ui.ICON_SEND, icon_color=ui.PURPLE) - text.bold("Proposal:") - text.mono(*split_proposal(proposals[0])) - await require_confirm(ctx, text, ButtonRequestType.SignTx) - - async def require_confirm_ballot(ctx, proposal, ballot): text = Text("Submit ballot", ui.ICON_SEND, icon_color=ui.PURPLE) text.bold("Ballot: {}".format(ballot)) text.bold("Proposal:") - text.mono(*split_proposal(proposal[0])) + text.mono(*split_proposal(proposal)) await require_confirm(ctx, text, ButtonRequestType.SignTx) @@ -98,15 +91,15 @@ async def require_confirm_ballot(ctx, proposal, ballot): async def show_proposals(ctx, proposals): first_page = const(0) pages = proposals + title = "Submit proposals" if len(proposals) > 1 else "Submit proposal" - paginator = paginate(show_proposal_page, len(pages), first_page, pages) + paginator = paginate(show_proposal_page, len(pages), first_page, pages, title) return await ctx.wait(paginator) @ui.layout -async def show_proposal_page(page: int, page_count: int, pages: list): - - text = Text("Submit proposals", ui.ICON_SEND, icon_color=ui.PURPLE) +async def show_proposal_page(page: int, page_count: int, pages: list, title: str): + text = Text(title, ui.ICON_SEND, icon_color=ui.PURPLE) text.bold("Proposal {}: ".format(page + 1)) text.mono(*split_proposal(pages[page])) content = Scrollpage(text, page, page_count) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 987ef553fb..98c4ec4ded 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -3,18 +3,16 @@ from micropython import const from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 -from trezor.messages import TezosContractType +from trezor.messages import TezosContractType, TezosBallotType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths from apps.tezos import CURVE, helpers, layout -from apps.tezos.writers import ( - write_bool, +from apps.common.writers import ( write_bytes, write_uint8, - write_uint16, - write_uint32, - write_uint64, + write_uint16_be, + write_uint32_be, ) PROPOSAL_LENGTH = const(32) @@ -65,12 +63,7 @@ async def sign_tx(ctx, msg, keychain): elif msg.proposal is not None: proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) - - # byte count larger than PROPOSAL_LENGTH indicates more than 1 proposal, use paginated screen - if msg.proposal.bytes_in_next_field > PROPOSAL_LENGTH: - await layout.show_proposals(ctx, proposed_protocols) - else: - await layout.require_confirm_proposal(ctx, proposed_protocols) + await layout.show_proposals(ctx, proposed_protocols) elif msg.ballot is not None: proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) @@ -127,25 +120,20 @@ def _get_address_from_contract(address): def _get_protocol_hash_from_msg(proposals): - # split the proposals - proposal_list = list( - [ - proposals[i : i + PROPOSAL_LENGTH] - for i in range(0, len(proposals), PROPOSAL_LENGTH) - ] - ) + print(proposals) + if type(proposals) is not list: + return helpers.base58_encode_check(proposals, prefix="P") return [ - helpers.base58_encode_check(proposal, prefix="P") for proposal in proposal_list + helpers.base58_encode_check(proposal, prefix="P") for proposal in proposals ] -def _get_ballot(encoded_ballot): - encoded_ballot = int(encoded_ballot[0]) - if encoded_ballot == 0: +def _get_ballot(ballot): + if ballot == TezosBallotType.Yay: return "yay" - elif encoded_ballot == 1: + elif ballot == TezosBallotType.Nay: return "nay" - elif encoded_ballot == 2: + elif ballot == TezosBallotType.Pass: return "pass" @@ -169,8 +157,8 @@ def _get_operation_bytes(w: bytearray, msg): _encode_common(w, msg.origination, "origination") write_bytes(w, msg.origination.manager_pubkey) _encode_zarith(w, msg.origination.balance) - _encode_bool(w, msg.origination.spendable) - _encode_bool(w, msg.origination.delegatable) + helpers.write_bool(w, msg.origination.spendable) + helpers.write_bool(w, msg.origination.delegatable) _encode_data_with_bool_prefix(w, msg.origination.delegate) _encode_data_with_bool_prefix(w, msg.origination.script) # delegation operation @@ -198,19 +186,12 @@ def _encode_contract_id(w: bytearray, contract_id): write_bytes(w, contract_id.hash) -def _encode_bool(w: bytearray, boolean): - if boolean: - write_uint8(w, 255) - else: - write_uint8(w, 0) - - def _encode_data_with_bool_prefix(w: bytearray, data): if data: - _encode_bool(w, True) + helpers.write_bool(w, True) write_bytes(w, data) else: - _encode_bool(w, False) + helpers.write_bool(w, False) def _encode_zarith(w: bytearray, num): @@ -230,9 +211,10 @@ def _encode_proposal(w: bytearray, proposal): write_uint8(w, proposal_tag) write_bytes(w, proposal.source) - write_uint32(w, proposal.period) - write_uint32(w, proposal.bytes_in_next_field) - write_bytes(w, proposal.proposals) + write_uint32_be(w, proposal.period) + write_uint32_be(w, _get_proposals_bytecount(proposal.proposals)) + for proposal_hash in proposal.proposals: + write_bytes(w, proposal_hash) def _encode_ballot(w: bytearray, ballot): @@ -240,6 +222,10 @@ def _encode_ballot(w: bytearray, ballot): write_uint8(w, ballot_tag) write_bytes(w, ballot.source) - write_uint32(w, ballot.period) + write_uint32_be(w, ballot.period) write_bytes(w, ballot.proposal) - write_bytes(w, ballot.ballot) + write_uint8(w, ballot.ballot) + + +def _get_proposals_bytecount(proposals): + return len(proposals) * PROPOSAL_LENGTH diff --git a/core/src/trezor/messages/TezosBallotOp.py b/core/src/trezor/messages/TezosBallotOp.py index 260bf2a4b5..cf6b969cd9 100644 --- a/core/src/trezor/messages/TezosBallotOp.py +++ b/core/src/trezor/messages/TezosBallotOp.py @@ -10,7 +10,7 @@ class TezosBallotOp(p.MessageType): source: bytes = None, period: int = None, proposal: bytes = None, - ballot: bytes = None, + ballot: int = None, ) -> None: self.source = source self.period = period @@ -23,5 +23,5 @@ class TezosBallotOp(p.MessageType): 1: ('source', p.BytesType, 0), 2: ('period', p.UVarintType, 0), 3: ('proposal', p.BytesType, 0), - 4: ('ballot', p.BytesType, 0), + 4: ('ballot', p.UVarintType, 0), } diff --git a/core/src/trezor/messages/TezosBallotType.py b/core/src/trezor/messages/TezosBallotType.py new file mode 100644 index 0000000000..1d955862f5 --- /dev/null +++ b/core/src/trezor/messages/TezosBallotType.py @@ -0,0 +1,5 @@ +# Automatically generated by pb2py +# fmt: off +Yay = 0 +Nay = 1 +Pass = 2 diff --git a/core/src/trezor/messages/TezosProposalOp.py b/core/src/trezor/messages/TezosProposalOp.py index 94f0dd532e..d533ce6c94 100644 --- a/core/src/trezor/messages/TezosProposalOp.py +++ b/core/src/trezor/messages/TezosProposalOp.py @@ -2,6 +2,12 @@ # fmt: off import protobuf as p +if __debug__: + try: + from typing import List + except ImportError: + List = None # type: ignore + class TezosProposalOp(p.MessageType): @@ -9,19 +15,16 @@ class TezosProposalOp(p.MessageType): self, source: bytes = None, period: int = None, - bytes_in_next_field: int = None, - proposals: bytes = None, + proposals: List[bytes] = None, ) -> None: self.source = source self.period = period - self.bytes_in_next_field = bytes_in_next_field - self.proposals = proposals + self.proposals = proposals if proposals is not None else [] @classmethod def get_fields(cls): return { 1: ('source', p.BytesType, 0), 2: ('period', p.UVarintType, 0), - 3: ('bytes_in_next_field', p.UVarintType, 0), - 4: ('proposals', p.BytesType, 0), + 4: ('proposals', p.BytesType, p.FLAG_REPEATED), } diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index d0478b5763..db5643c388 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -23,6 +23,7 @@ from trezorlib.tools import parse_path from .common import TrezorTest TEZOS_PATH = parse_path("m/44'/1729'/0'") +TEZOS_PATH_10 = parse_path("m/44'/1729'/10'") @pytest.mark.tezos @@ -194,3 +195,140 @@ class TestMsgTezosSignTx(TrezorTest): assert ( resp.operation_hash == "oocgc3hyKsGHPsw6WFWJpWT8jBwQLtebQAXF27KNisThkzoj635" ) + + def test_tezos_sign_tx_proposal(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc81", + "proposal": { + "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", + "period": 17, + "proposals": ["dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572"], + }, + }, + ), + ) + assert ( + resp.signature + == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" + ) + assert ( + resp.sig_op_contents.hex() + == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" + ) + assert ( + resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" + ) + + def test_tezos_sign_tx_multiple_proposals(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df7", + "proposal": { + "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", + "period": 17, + "proposals": ["2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", + "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762"], + }, + }, + ), + ) + assert ( + resp.signature + == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" + ) + assert ( + resp.sig_op_contents.hex() + == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" + ) + assert ( + resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" + ) + + + def test_tezos_sing_tx_ballot_yay(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 0 + }, + } + ) + ) + + assert ( + resp.signature + == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" + ) + + def test_tezos_sing_tx_ballot_nay(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 1 + }, + } + ) + ) + assert ( + resp.signature + == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" + ) + + def test_tezos_sing_tx_ballot_pass(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 2 + }, + } + ) + ) + + assert ( + resp.signature + == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" + ) + From c588d0b4f3244ad73cad2355e25cb5f66516987f Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 08:41:38 +0200 Subject: [PATCH 04/13] requested changes #2 --- core/src/apps/tezos/sign_tx.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 98c4ec4ded..f6aed917ef 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -62,11 +62,11 @@ async def sign_tx(ctx, msg, keychain): ) elif msg.proposal is not None: - proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) + proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals] await layout.show_proposals(ctx, proposed_protocols) elif msg.ballot is not None: - proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) + proposed_protocol = _get_protocol_hash(msg.ballot.proposal) submitted_ballot = _get_ballot(msg.ballot.ballot) await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot) @@ -119,13 +119,8 @@ def _get_address_from_contract(address): raise wire.DataError("Invalid contract type") -def _get_protocol_hash_from_msg(proposals): - print(proposals) - if type(proposals) is not list: - return helpers.base58_encode_check(proposals, prefix="P") - return [ - helpers.base58_encode_check(proposal, prefix="P") for proposal in proposals - ] +def _get_protocol_hash(proposal): + return helpers.base58_encode_check(proposal, prefix="P") def _get_ballot(ballot): @@ -212,7 +207,7 @@ def _encode_proposal(w: bytearray, proposal): write_uint8(w, proposal_tag) write_bytes(w, proposal.source) write_uint32_be(w, proposal.period) - write_uint32_be(w, _get_proposals_bytecount(proposal.proposals)) + write_uint32_be(w, len(proposal.proposals) * PROPOSAL_LENGTH) for proposal_hash in proposal.proposals: write_bytes(w, proposal_hash) @@ -225,7 +220,3 @@ def _encode_ballot(w: bytearray, ballot): write_uint32_be(w, ballot.period) write_bytes(w, ballot.proposal) write_uint8(w, ballot.ballot) - - -def _get_proposals_bytecount(proposals): - return len(proposals) * PROPOSAL_LENGTH From da7bb8abf8d9bcba8a095c624200d6c0f2e6d88a Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 12:32:43 +0200 Subject: [PATCH 05/13] fix path validation and update unit tests --- core/src/apps/tezos/helpers.py | 5 +++-- core/tests/test_apps.tezos.address.py | 6 +++++- core/tests/test_apps.tezos.encode.py | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index e93b493881..4640d7eb30 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -47,15 +47,16 @@ def validate_full_path(path: list) -> bool: where `a` is an account index from 0 to 1 000 000. """ length = len(path) + print(path) if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: return False if path[1] != 1729 | HARDENED: return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: + if length == 3 and (path[2] < HARDENED or path[2] > 1000000 | HARDENED): return False - if length > 3 and (path[3] < HARDENED or path[3] > 1000000 | HARDENED): + if length == 4 and (path[2] != 0 | HARDENED or path[3] < HARDENED or path[3] > 1000000 | HARDENED): return False return True diff --git a/core/tests/test_apps.tezos.address.py b/core/tests/test_apps.tezos.address.py index 9b093f9e0b..c3870fd313 100644 --- a/core/tests/test_apps.tezos.address.py +++ b/core/tests/test_apps.tezos.address.py @@ -46,10 +46,11 @@ class TestTezosAddress(unittest.TestCase): [44 | HARDENED], [44 | HARDENED, 1729 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 0], - [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 1, 0], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0, 0], + [44 | HARDENED, 1729 | HARDENED, 1 | HARDENED, 1 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 9999000 | HARDENED], [44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0], [1 | HARDENED, 1 | HARDENED, 1 | HARDENED], @@ -58,6 +59,9 @@ class TestTezosAddress(unittest.TestCase): [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 3 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 9 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 3 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 9 | HARDENED], ] for path in incorrect_paths: diff --git a/core/tests/test_apps.tezos.encode.py b/core/tests/test_apps.tezos.encode.py index ee875d3757..16f1142f4d 100644 --- a/core/tests/test_apps.tezos.encode.py +++ b/core/tests/test_apps.tezos.encode.py @@ -4,9 +4,8 @@ from common import * from trezor.messages import TezosContractType from trezor.messages.TezosContractID import TezosContractID -from apps.tezos.helpers import base58_decode_check, base58_encode_check +from apps.tezos.helpers import base58_decode_check, base58_encode_check, write_bool from apps.tezos.sign_tx import ( - _encode_bool, _encode_contract_id, _encode_data_with_bool_prefix, _encode_zarith, @@ -35,11 +34,11 @@ class TestTezosEncoding(unittest.TestCase): def test_tezos_encode_bool(self): w = bytearray() - _encode_bool(w, True) + write_bool(w, True) self.assertEqual(bytes(w), bytes([255])) w = bytearray() - _encode_bool(w, False) + write_bool(w, False) self.assertEqual(bytes(w), bytes([0])) def test_tezos_encode_contract_id(self): From 2dd1ae64914bb259bd9b92493099ff81c90c421b Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:25:42 +0200 Subject: [PATCH 06/13] refactor path validation --- core/src/apps/tezos/helpers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 4640d7eb30..6cf5e331ba 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -54,10 +54,14 @@ def validate_full_path(path: list) -> bool: return False if path[1] != 1729 | HARDENED: return False - if length == 3 and (path[2] < HARDENED or path[2] > 1000000 | HARDENED): - return False - if length == 4 and (path[2] != 0 | HARDENED or path[3] < HARDENED or path[3] > 1000000 | HARDENED): - return False + if length == 3: + if path[2] < HARDENED or path[2] > 1000000 | HARDENED: + return False + if length == 4: + if path[2] != 0 | HARDENED: + return False + if path[3] < HARDENED or path[3] > 1000000 | HARDENED: + return False return True From dce60da6ec00046b84ff6dfdbb5f0246daf68e18 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:37:11 +0200 Subject: [PATCH 07/13] Added comment to path validation --- core/src/apps/tezos/helpers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 6cf5e331ba..631947a012 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -45,6 +45,8 @@ def validate_full_path(path: list) -> bool: """ Validates derivation path to equal 44'/1729'/a', where `a` is an account index from 0 to 1 000 000. + Additional component added to allow ledger migration + 44'/1729'/0'/b' where `b` is an account index from 0 to 1 000 000 """ length = len(path) print(path) From 03790e992fbb8608c439ba91eed43458a0a8edeb Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:40:04 +0200 Subject: [PATCH 08/13] reformated with black --- .../device_tests/test_msg_tezos_sign_tx.py | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index db5643c388..7bcee80876 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -209,21 +209,23 @@ class TestMsgTezosSignTx(TrezorTest): "proposal": { "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", "period": 17, - "proposals": ["dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572"], + "proposals": [ + "dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572" + ], }, }, ), ) assert ( - resp.signature - == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" + resp.signature + == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" ) assert ( - resp.sig_op_contents.hex() - == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" + resp.sig_op_contents.hex() + == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" ) assert ( - resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" + resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" ) def test_tezos_sign_tx_multiple_proposals(self): @@ -239,25 +241,26 @@ class TestMsgTezosSignTx(TrezorTest): "proposal": { "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", "period": 17, - "proposals": ["2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", - "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762"], + "proposals": [ + "2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", + "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762", + ], }, }, ), ) assert ( - resp.signature - == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" + resp.signature + == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" ) assert ( - resp.sig_op_contents.hex() - == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" + resp.sig_op_contents.hex() + == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" ) assert ( - resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" + resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" ) - def test_tezos_sing_tx_ballot_yay(self): self.setup_mnemonic_allallall() @@ -272,15 +275,15 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 0 + "ballot": 0, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" + resp.signature + == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" ) def test_tezos_sing_tx_ballot_nay(self): @@ -297,14 +300,14 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 1 + "ballot": 1, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" + resp.signature + == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" ) def test_tezos_sing_tx_ballot_pass(self): @@ -321,14 +324,13 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 2 + "ballot": 2, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" + resp.signature + == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" ) - From 8ee605484e13b1a51fe1b31e56e3710e23572eaf Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:45:36 +0200 Subject: [PATCH 09/13] remove stray print --- core/src/apps/tezos/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 631947a012..73bd02d1f8 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -49,7 +49,6 @@ def validate_full_path(path: list) -> bool: 44'/1729'/0'/b' where `b` is an account index from 0 to 1 000 000 """ length = len(path) - print(path) if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: From 1e7357db8026346c8d4651c8b5b969eed9df04c0 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:03:24 +0200 Subject: [PATCH 10/13] core/ui: fix crash when trying to scroll with only one page --- core/src/trezor/ui/scroll.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/trezor/ui/scroll.py b/core/src/trezor/ui/scroll.py index 89d2fad39b..d144f76d7c 100644 --- a/core/src/trezor/ui/scroll.py +++ b/core/src/trezor/ui/scroll.py @@ -21,9 +21,9 @@ async def change_page(page, page_count): else: s = await swipe if s == SWIPE_UP: - return page + 1 # scroll down + return min(page + 1, page_count - 1) # scroll down elif s == SWIPE_DOWN: - return page - 1 # scroll up + return max(page - 1, 0) # scroll up async def paginate(render_page, page_count, page=0, *args): From 1e1e6513a03ba2504292547c91efff8a9aae844e Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:43:33 +0200 Subject: [PATCH 11/13] core/tezos: add ButtonRequest in front of proposal paginator --- core/src/apps/tezos/layout.py | 8 +++++--- core/src/apps/tezos/sign_tx.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 315875fea7..325d73b26d 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -1,8 +1,9 @@ from micropython import const from trezor import ui, wire -from trezor.messages import ButtonRequestType -from trezor.ui.confirm import CANCELLED, CONFIRMED, ConfirmDialog +from trezor.messages import ButtonRequestType, MessageType +from trezor.messages.ButtonRequest import ButtonRequest +from trezor.ui.confirm import CANCELLED, ConfirmDialog from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.ui.text import Text from trezor.utils import chunks, format_amount @@ -88,7 +89,8 @@ async def require_confirm_ballot(ctx, proposal, ballot): # use, when there are more then one proposals in one operation -async def show_proposals(ctx, proposals): +async def require_confirm_proposals(ctx, proposals): + await ctx.call(ButtonRequest(code=ButtonRequestType.SignTx), MessageType.ButtonAck) first_page = const(0) pages = proposals title = "Submit proposals" if len(proposals) > 1 else "Submit proposal" diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index f6aed917ef..ff575e3187 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -63,7 +63,7 @@ async def sign_tx(ctx, msg, keychain): elif msg.proposal is not None: proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals] - await layout.show_proposals(ctx, proposed_protocols) + await layout.require_confirm_proposals(ctx, proposed_protocols) elif msg.ballot is not None: proposed_protocol = _get_protocol_hash(msg.ballot.proposal) From 3e74ed927bbfbafb668687d8dca0dcdfd48cf885 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:44:09 +0200 Subject: [PATCH 12/13] python/tezos: add input flow for paginators --- .../tests/device_tests/test_msg_tezos_sign_tx.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index 7bcee80876..bee4700ce0 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -14,6 +14,8 @@ # You should have received a copy of the License along with this library. # If not, see . +import time + import pytest from trezorlib import messages, tezos @@ -196,9 +198,19 @@ class TestMsgTezosSignTx(TrezorTest): resp.operation_hash == "oocgc3hyKsGHPsw6WFWJpWT8jBwQLtebQAXF27KNisThkzoj635" ) + def input_flow(self, num_pages): + yield + time.sleep(1) + for _ in range(num_pages - 1): + self.client.debug.swipe_down() + time.sleep(1) + self.client.debug.press_yes() + + def test_tezos_sign_tx_proposal(self): self.setup_mnemonic_allallall() + self.client.set_input_flow(self.input_flow(num_pages=1)) resp = tezos.sign_tx( self.client, TEZOS_PATH_10, @@ -231,6 +243,7 @@ class TestMsgTezosSignTx(TrezorTest): def test_tezos_sign_tx_multiple_proposals(self): self.setup_mnemonic_allallall() + self.client.set_input_flow(self.input_flow(num_pages=2)) resp = tezos.sign_tx( self.client, TEZOS_PATH_10, From 834a483e1a30284b9b2a87692affb769612b8a47 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:53:57 +0200 Subject: [PATCH 13/13] tezos: make style --- core/src/apps/tezos/sign_tx.py | 4 ++-- python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index ff575e3187..04ef8d5470 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -3,17 +3,17 @@ from micropython import const from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 -from trezor.messages import TezosContractType, TezosBallotType +from trezor.messages import TezosBallotType, TezosContractType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths -from apps.tezos import CURVE, helpers, layout from apps.common.writers import ( write_bytes, write_uint8, write_uint16_be, write_uint32_be, ) +from apps.tezos import CURVE, helpers, layout PROPOSAL_LENGTH = const(32) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index bee4700ce0..ecd6101a76 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -206,7 +206,6 @@ class TestMsgTezosSignTx(TrezorTest): time.sleep(1) self.client.debug.press_yes() - def test_tezos_sign_tx_proposal(self): self.setup_mnemonic_allallall()