Example #1
0
def _check_rsig_data(state: State,
                     rsig_data: MoneroTransactionRsigData) -> None:
    """
    There are two types of monero ring confidential transactions:
    1. RCTTypeFull = 1 (used if num_inputs == 1 && Borromean)
    2. RCTTypeSimple = 2 (for num_inputs > 1 || !Borromean)

    and four types of range proofs (set in `rsig_data.rsig_type`):
    1. RangeProofBorromean = 0
    2. RangeProofBulletproof = 1
    3. RangeProofMultiOutputBulletproof = 2
    4. RangeProofPaddedBulletproof = 3

    The current code supports only HF9, HF10 thus TX type is always simple
    and RCT algorithm is always Bulletproof.
    """
    state.rsig_grouping = rsig_data.grouping

    if rsig_data.rsig_type == 0:
        raise ValueError("Borromean range sig not supported")

    elif rsig_data.rsig_type not in (1, 2, 3):
        raise ValueError("Unknown rsig type")

    state.tx_type = signing.RctType.CLSAG
    if rsig_data.bp_version == 4:
        state.rsig_is_bp_plus = True
        state.tx_type = signing.RctType.RCTTypeBulletproofPlus

    if state.output_count > 2:
        state.rsig_offload = True

    _check_grouping(state)
async def init_transaction(
    state: State,
    address_n: list,
    network_type: int,
    tsx_data: MoneroTransactionData,
    keychain,
) -> MoneroTransactionInitAck:
    from apps.monero.signing import offloading_keys
    from apps.common import paths

    await paths.validate_path(state.ctx, misc.validate_full_path, keychain,
                              address_n, CURVE)

    state.creds = misc.get_creds(keychain, address_n, network_type)
    state.client_version = tsx_data.client_version or 0
    if state.client_version == 0:
        raise ValueError("Client version not supported")

    state.fee = state.fee if state.fee > 0 else 0
    state.tx_priv = crypto.random_scalar()
    state.tx_pub = crypto.scalarmult_base(state.tx_priv)
    state.mem_trace(1)

    state.input_count = tsx_data.num_inputs
    state.output_count = len(tsx_data.outputs)
    state.progress_total = 4 + 3 * state.input_count + state.output_count
    state.progress_cur = 0

    # Ask for confirmation
    await confirms.require_confirm_transaction(state.ctx, state, tsx_data,
                                               state.creds.network_type)
    state.creds.address = None
    state.creds.network_type = None
    gc.collect()
    state.mem_trace(3)

    # Basic transaction parameters
    state.output_change = tsx_data.change_dts
    state.mixin = tsx_data.mixin
    state.fee = tsx_data.fee
    state.account_idx = tsx_data.account
    state.last_step = state.STEP_INIT
    if tsx_data.hard_fork:
        state.hard_fork = tsx_data.hard_fork

    state.tx_type = (signing.RctType.CLSAG if state.hard_fork >= 13 else
                     signing.RctType.Bulletproof2)

    # Ensure change is correct
    _check_change(state, tsx_data.outputs)

    # At least two outpus are required, this applies also for sweep txs
    # where one fake output is added. See _check_change for more info
    if state.output_count < 2:
        raise signing.NotEnoughOutputsError(
            "At least two outputs are required")

    _check_rsig_data(state, tsx_data.rsig_data)
    _check_subaddresses(state, tsx_data.outputs)

    # Extra processing, payment id
    _process_payment_id(state, tsx_data)
    _compute_sec_keys(state, tsx_data)
    gc.collect()

    # Iterative tx_prefix_hash hash computation
    state.tx_prefix_hasher.uvarint(
        2)  # current Monero transaction format (RingCT = 2)
    state.tx_prefix_hasher.uvarint(tsx_data.unlock_time)
    state.tx_prefix_hasher.uvarint(state.input_count)  # ContainerType, size
    state.mem_trace(10, True)

    # Final message hasher
    state.full_message_hasher.init()
    state.full_message_hasher.set_type_fee(state.tx_type, state.fee)

    # Sub address precomputation
    if tsx_data.account is not None and tsx_data.minor_indices:
        _precompute_subaddr(state, tsx_data.account, tsx_data.minor_indices)
    state.mem_trace(5, True)

    # HMACs all outputs to disallow tampering.
    # Each HMAC is then sent alongside the output
    # and trezor validates it.
    hmacs = []
    for idx in range(state.output_count):
        c_hmac = offloading_keys.gen_hmac_tsxdest(state.key_hmac,
                                                  tsx_data.outputs[idx], idx)
        hmacs.append(c_hmac)
        gc.collect()

    state.mem_trace(6)

    from trezor.messages.MoneroTransactionInitAck import MoneroTransactionInitAck
    from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData

    rsig_data = MoneroTransactionRsigData(offload_type=state.rsig_offload)

    return MoneroTransactionInitAck(hmacs=hmacs, rsig_data=rsig_data)