mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-16 03:18:09 +00:00
Add support for tezos voting operations (proposal and ballot)
This commit is contained in:
parent
67907b6c4c
commit
a0d89c0785
@ -22,6 +22,8 @@ TEZOS_PREFIX_BYTES = {
|
||||
"edsig": [9, 245, 205, 134, 18],
|
||||
# operation hash
|
||||
"o": [5, 116],
|
||||
# protocola hash
|
||||
"P": [2, 170],
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
24
src/apps/tezos/writers.py
Normal file
24
src/apps/tezos/writers.py
Normal 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)
|
27
src/trezor/messages/TezosBallotOp.py
Normal file
27
src/trezor/messages/TezosBallotOp.py
Normal 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),
|
||||
}
|
27
src/trezor/messages/TezosProposalOp.py
Normal file
27
src/trezor/messages/TezosProposalOp.py
Normal 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),
|
||||
}
|
@ -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),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user