def _rsig_bp(state: State) -> bytes:
    """Bulletproof calculation in trezor"""
    from apps.monero.xmr import range_signatures

    rsig = range_signatures.prove_range_bp_batch(state.output_amounts,
                                                 state.output_masks,
                                                 state.rsig_is_bp_plus)
    state.mem_trace("post-bp" if __debug__ else None, collect=True)

    # Incremental BP hashing
    # BP is hashed with raw=False as hash does not contain L, R
    # array sizes compared to the serialized bulletproof format
    # thus direct serialization cannot be used.
    state.full_message_hasher.rsig_val(rsig, raw=False)
    state.mem_trace("post-bp-hash" if __debug__ else None, collect=True)

    rsig = _dump_rsig_bp_plus(
        rsig) if state.rsig_is_bp_plus else _dump_rsig_bp(rsig)
    state.mem_trace(f"post-bp-ser, size: {len(rsig)}" if __debug__ else None,
                    collect=True)

    # state cleanup
    state.output_masks = []
    state.output_amounts = []
    return rsig
Beispiel #2
0
def _range_proof(state, amount, rsig_data):
    """
    Computes rangeproof
    In order to optimize incremental transaction build, the mask computation is changed compared
    to the official Monero code. In the official code, the input pedersen commitments are computed
    after range proof in such a way summed masks for commitments (alpha) and rangeproofs (ai) are equal.

    In order to save roundtrips we compute commitments randomly and then for the last rangeproof
    a[63] = (\\sum_{i=0}^{num_inp}alpha_i - \\sum_{i=0}^{num_outs-1} amasks_i) - \\sum_{i=0}^{62}a_i

    The range proof is incrementally hashed to the final_message.
    """
    from apps.monero.xmr import range_signatures

    mask = state.output_masks[state.current_output_index]
    provided_rsig = None
    if rsig_data and rsig_data.rsig and len(rsig_data.rsig) > 0:
        provided_rsig = rsig_data.rsig
    if not state.rsig_offload and provided_rsig:
        raise signing.Error("Provided unexpected rsig")

    # Batching
    bidx = _get_rsig_batch(state, state.current_output_index)
    batch_size = state.rsig_grouping[bidx]
    last_in_batch = _is_last_in_batch(state, state.current_output_index, bidx)
    if state.rsig_offload and provided_rsig and not last_in_batch:
        raise signing.Error("Provided rsig too early")
    if state.rsig_offload and last_in_batch and not provided_rsig:
        raise signing.Error("Rsig expected, not provided")

    # Batch not finished, skip range sig generation now
    if not last_in_batch:
        return None, mask

    # Rangeproof
    # Pedersen commitment on the value, mask from the commitment, range signature.
    C, rsig = None, None

    state.mem_trace("pre-rproof" if __debug__ else None, collect=True)
    if state.rsig_type == RsigType.Bulletproof and not state.rsig_offload:
        """Bulletproof calculation in trezor"""
        rsig = range_signatures.prove_range_bp_batch(state.output_amounts,
                                                     state.output_masks)
        state.mem_trace("post-bp" if __debug__ else None, collect=True)

        # Incremental BP hashing
        # BP is hashed with raw=False as hash does not contain L, R
        # array sizes compared to the serialized bulletproof format
        # thus direct serialization cannot be used.
        state.full_message_hasher.rsig_val(rsig, True, raw=False)
        state.mem_trace("post-bp-hash" if __debug__ else None, collect=True)

        rsig = _dump_rsig_bp(rsig)
        state.mem_trace("post-bp-ser, size: %s" %
                        len(rsig) if __debug__ else None,
                        collect=True)

    elif state.rsig_type == RsigType.Borromean and not state.rsig_offload:
        """Borromean calculation in trezor"""
        C, mask, rsig = range_signatures.prove_range_borromean(amount, mask)
        del range_signatures

        # Incremental hashing
        state.full_message_hasher.rsig_val(rsig, False, raw=True)
        _check_out_commitment(state, amount, mask, C)

    elif state.rsig_type == RsigType.Bulletproof and state.rsig_offload:
        """Bulletproof calculated on host, verify in trezor"""
        from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof

        # TODO this should be tested
        # last_in_batch = True (see above) so this is fine
        masks = state.output_masks[1 + state.current_output_index -
                                   batch_size:1 + state.current_output_index]
        bp_obj = serialize.parse_msg(rsig_data.rsig, Bulletproof)
        rsig_data.rsig = None

        # BP is hashed with raw=False as hash does not contain L, R
        # array sizes compared to the serialized bulletproof format
        # thus direct serialization cannot be used.
        state.full_message_hasher.rsig_val(bp_obj, True, raw=False)
        res = range_signatures.verify_bp(bp_obj, state.output_amounts, masks)
        utils.ensure(res, "BP verification fail")
        state.mem_trace("BP verified" if __debug__ else None, collect=True)
        del (bp_obj, range_signatures)

    elif state.rsig_type == RsigType.Borromean and state.rsig_offload:
        """Borromean offloading not supported"""
        raise signing.Error(
            "Unsupported rsig state (Borromean offloaded is not supported)")

    else:
        raise signing.Error("Unexpected rsig state")

    state.mem_trace("rproof" if __debug__ else None, collect=True)
    if state.current_output_index + 1 == state.output_count:
        # output masks and amounts are not needed anymore
        state.output_amounts = []
        state.output_masks = []
    return rsig, mask