You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.4 KiB
180 lines
5.4 KiB
"""
|
|
All outputs were set in this phase. This step serializes tx pub keys
|
|
into the tx extra field and then hashes it into the prefix hash.
|
|
The prefix hash is then complete.
|
|
"""
|
|
|
|
import gc
|
|
|
|
from trezor import utils
|
|
|
|
from apps.monero.layout import confirms
|
|
from apps.monero.signing import RctType
|
|
from apps.monero.xmr import crypto
|
|
|
|
from .state import State
|
|
|
|
if False:
|
|
from trezor.messages.MoneroTransactionAllOutSetAck import (
|
|
MoneroTransactionAllOutSetAck,
|
|
)
|
|
|
|
|
|
async def all_outputs_set(state: State) -> MoneroTransactionAllOutSetAck:
|
|
state.mem_trace(0)
|
|
|
|
await confirms.transaction_step(state, state.STEP_ALL_OUT)
|
|
state.mem_trace(1)
|
|
|
|
_validate(state)
|
|
state.is_processing_offloaded = False
|
|
state.mem_trace(2)
|
|
|
|
extra_b = _set_tx_extra(state)
|
|
# tx public keys not needed anymore
|
|
state.additional_tx_public_keys = None
|
|
state.tx_pub = None
|
|
state.rsig_grouping = None
|
|
state.rsig_offload = None
|
|
gc.collect()
|
|
state.mem_trace(3)
|
|
|
|
# Completes the transaction prefix hash by including extra
|
|
_set_tx_prefix(state, extra_b)
|
|
state.output_change = None
|
|
gc.collect()
|
|
state.mem_trace(4)
|
|
|
|
# In the multisig mode here needs to be a check whether currently computed
|
|
# transaction prefix matches expected transaction prefix sent in the
|
|
# init step.
|
|
|
|
from trezor.messages.MoneroRingCtSig import MoneroRingCtSig
|
|
from trezor.messages.MoneroTransactionAllOutSetAck import (
|
|
MoneroTransactionAllOutSetAck,
|
|
)
|
|
|
|
# Initializes RCTsig structure (fee, tx prefix hash, type)
|
|
rv_pb = MoneroRingCtSig(
|
|
txn_fee=state.fee, message=state.tx_prefix_hash, rv_type=RctType.Bulletproof2,
|
|
)
|
|
|
|
_out_pk(state)
|
|
state.full_message_hasher.rctsig_base_done()
|
|
state.current_output_index = None
|
|
state.current_input_index = -1
|
|
|
|
state.full_message = state.full_message_hasher.get_digest()
|
|
state.full_message_hasher = None
|
|
state.output_pk_commitments = None
|
|
state.summary_outs_money = None
|
|
state.summary_inputs_money = None
|
|
state.fee = None
|
|
state.last_ki = None
|
|
state.last_step = state.STEP_ALL_OUT
|
|
|
|
return MoneroTransactionAllOutSetAck(
|
|
extra=extra_b,
|
|
tx_prefix_hash=state.tx_prefix_hash,
|
|
rv=rv_pb,
|
|
full_message_hash=state.full_message,
|
|
)
|
|
|
|
|
|
def _validate(state: State):
|
|
if state.last_step != state.STEP_OUT:
|
|
raise ValueError("Invalid state transition")
|
|
if state.current_output_index + 1 != state.output_count:
|
|
raise ValueError("Invalid out num")
|
|
|
|
# Fee test
|
|
if state.fee != (state.summary_inputs_money - state.summary_outs_money):
|
|
raise ValueError(
|
|
"Fee invalid %s vs %s, out: %s"
|
|
% (
|
|
state.fee,
|
|
state.summary_inputs_money - state.summary_outs_money,
|
|
state.summary_outs_money,
|
|
)
|
|
)
|
|
|
|
if state.summary_outs_money > state.summary_inputs_money:
|
|
raise ValueError(
|
|
"Transaction inputs money (%s) less than outputs money (%s)"
|
|
% (state.summary_inputs_money, state.summary_outs_money)
|
|
)
|
|
|
|
|
|
def _set_tx_extra(state: State) -> bytes:
|
|
"""
|
|
Sets tx public keys into transaction's extra.
|
|
Extra field is supposed to be sorted (by sort_tx_extra() in the Monero)
|
|
Tag ordering: TX_EXTRA_TAG_PUBKEY, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS, TX_EXTRA_NONCE
|
|
"""
|
|
from apps.monero.xmr.serialize import int_serialize
|
|
|
|
# Extra buffer length computation
|
|
# TX_EXTRA_TAG_PUBKEY (1B) | tx_pub_key (32B)
|
|
extra_size = 33
|
|
offset = 0
|
|
num_keys = 0
|
|
len_size = 0
|
|
|
|
if state.need_additional_txkeys:
|
|
num_keys = len(state.additional_tx_public_keys)
|
|
len_size = int_serialize.uvarint_size(num_keys)
|
|
|
|
# TX_EXTRA_TAG_ADDITIONAL_PUBKEYS (1B) | varint | keys
|
|
extra_size += 1 + len_size + 32 * num_keys
|
|
|
|
if state.extra_nonce:
|
|
extra_size += len(state.extra_nonce)
|
|
|
|
extra = bytearray(extra_size)
|
|
extra[0] = 1 # TX_EXTRA_TAG_PUBKEY
|
|
crypto.encodepoint_into(memoryview(extra)[1:], state.tx_pub)
|
|
offset += 33
|
|
|
|
if state.need_additional_txkeys:
|
|
extra[offset] = 0x4 # TX_EXTRA_TAG_ADDITIONAL_PUBKEYS
|
|
int_serialize.dump_uvarint_b_into(num_keys, extra, offset + 1)
|
|
offset += 1 + len_size
|
|
|
|
for idx in range(num_keys):
|
|
extra[offset : offset + 32] = state.additional_tx_public_keys[idx]
|
|
offset += 32
|
|
|
|
if state.extra_nonce:
|
|
utils.memcpy(extra, offset, state.extra_nonce, 0, len(state.extra_nonce))
|
|
state.extra_nonce = None
|
|
|
|
return extra
|
|
|
|
|
|
def _set_tx_prefix(state: State, extra: bytes):
|
|
"""
|
|
Adds `extra` to the tx_prefix_hash, which is the last needed item,
|
|
so the tx_prefix_hash is now complete and can be incorporated
|
|
into full_message_hash.
|
|
"""
|
|
# Serializing "extra" type as BlobType.
|
|
# uvarint(len(extra)) || extra
|
|
state.tx_prefix_hasher.uvarint(len(extra))
|
|
state.tx_prefix_hasher.buffer(extra)
|
|
|
|
state.tx_prefix_hash = state.tx_prefix_hasher.get_digest()
|
|
state.tx_prefix_hasher = None
|
|
|
|
state.full_message_hasher.set_message(state.tx_prefix_hash)
|
|
|
|
|
|
def _out_pk(state: State):
|
|
"""
|
|
Hashes out_pk into the full message.
|
|
"""
|
|
if state.output_count != len(state.output_pk_commitments):
|
|
raise ValueError("Invalid number of ecdh")
|
|
|
|
for out in state.output_pk_commitments:
|
|
state.full_message_hasher.set_out_pk_commitment(out)
|