1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-28 17:18:29 +00:00

Merge remote-tracking branch 'core-local/voting' into voting

This commit is contained in:
Adrian Nagy 2019-04-16 11:10:52 +02:00
commit 0c630831dc
7 changed files with 212 additions and 3 deletions

View File

@ -21,6 +21,8 @@ TEZOS_PREFIX_BYTES = {
"edsig": [9, 245, 205, 134, 18], "edsig": [9, 245, 205, 134, 18],
# operation hash # operation hash
"o": [5, 116], "o": [5, 116],
# protocola hash
"P": [2, 170],
} }

View File

@ -1,5 +1,9 @@
from trezor import ui from micropython import const
from trezor import ui, wire
from trezor.messages import ButtonRequestType 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.ui.text import Text
from trezor.utils import chunks, format_amount from trezor.utils import chunks, format_amount
@ -66,6 +70,51 @@ def split_address(address):
return chunks(address, 18) return chunks(address, 18)
def split_proposal(proposal):
return chunks(proposal, 17)
def format_tezos_amount(value): def format_tezos_amount(value):
formatted_value = format_amount(value, TEZOS_AMOUNT_DIVISIBILITY) formatted_value = format_amount(value, TEZOS_AMOUNT_DIVISIBILITY)
return formatted_value + " XTZ" 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()

View File

@ -1,3 +1,5 @@
from micropython import const
from trezor import wire from trezor import wire
from trezor.crypto import hashlib from trezor.crypto import hashlib
from trezor.crypto.curve import ed25519 from trezor.crypto.curve import ed25519
@ -5,8 +7,17 @@ from trezor.messages import TezosContractType
from trezor.messages.TezosSignedTx import TezosSignedTx from trezor.messages.TezosSignedTx import TezosSignedTx
from apps.common import paths from apps.common import paths
from apps.common.writers import write_bytes, write_uint8
from apps.tezos import CURVE, helpers, layout from apps.tezos import CURVE, 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): async def sign_tx(ctx, msg, keychain):
@ -52,6 +63,20 @@ async def sign_tx(ctx, msg, keychain):
ctx, source, msg.delegation.fee 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: else:
raise wire.DataError("Invalid operation") raise wire.DataError("Invalid operation")
@ -101,11 +126,34 @@ def _get_address_from_contract(address):
raise wire.DataError("Invalid contract type") 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): def _get_operation_bytes(w: bytearray, msg):
write_bytes(w, msg.branch) write_bytes(w, msg.branch)
# when the account sends first operation in lifetime, # 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: if msg.reveal is not None:
_encode_common(w, msg.reveal, "reveal") _encode_common(w, msg.reveal, "reveal")
write_bytes(w, msg.reveal.public_key) write_bytes(w, msg.reveal.public_key)
@ -129,6 +177,10 @@ def _get_operation_bytes(w: bytearray, msg):
elif msg.delegation is not None: elif msg.delegation is not None:
_encode_common(w, msg.delegation, "delegation") _encode_common(w, msg.delegation, "delegation")
_encode_data_with_bool_prefix(w, msg.delegation.delegate) _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): def _encode_common(w: bytearray, operation, str_operation):
@ -171,3 +223,23 @@ def _encode_zarith(w: bytearray, num):
break break
write_uint8(w, 128 | byte) 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)

View File

@ -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)

View File

@ -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),
}

View File

@ -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),
}

View File

@ -2,8 +2,10 @@
# fmt: off # fmt: off
import protobuf as p import protobuf as p
from .TezosBallotOp import TezosBallotOp
from .TezosDelegationOp import TezosDelegationOp from .TezosDelegationOp import TezosDelegationOp
from .TezosOriginationOp import TezosOriginationOp from .TezosOriginationOp import TezosOriginationOp
from .TezosProposalOp import TezosProposalOp
from .TezosRevealOp import TezosRevealOp from .TezosRevealOp import TezosRevealOp
from .TezosTransactionOp import TezosTransactionOp from .TezosTransactionOp import TezosTransactionOp
@ -25,6 +27,8 @@ class TezosSignTx(p.MessageType):
transaction: TezosTransactionOp = None, transaction: TezosTransactionOp = None,
origination: TezosOriginationOp = None, origination: TezosOriginationOp = None,
delegation: TezosDelegationOp = None, delegation: TezosDelegationOp = None,
proposal: TezosProposalOp = None,
ballot: TezosBallotOp = None,
) -> None: ) -> None:
self.address_n = address_n if address_n is not None else [] self.address_n = address_n if address_n is not None else []
self.branch = branch self.branch = branch
@ -32,6 +36,8 @@ class TezosSignTx(p.MessageType):
self.transaction = transaction self.transaction = transaction
self.origination = origination self.origination = origination
self.delegation = delegation self.delegation = delegation
self.proposal = proposal
self.ballot = ballot
@classmethod @classmethod
def get_fields(cls): def get_fields(cls):
@ -42,4 +48,6 @@ class TezosSignTx(p.MessageType):
4: ('transaction', TezosTransactionOp, 0), 4: ('transaction', TezosTransactionOp, 0),
5: ('origination', TezosOriginationOp, 0), 5: ('origination', TezosOriginationOp, 0),
6: ('delegation', TezosDelegationOp, 0), 6: ('delegation', TezosDelegationOp, 0),
7: ('proposal', TezosProposalOp, 0),
8: ('ballot', TezosBallotOp, 0),
} }