Files
salvium/src/carrot_impl/tx_proposal_utils.h
T
2025-08-04 11:01:33 +01:00

231 lines
9.1 KiB
C++

// Copyright (c) 2025, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @file Utilities for creating Carrot transaction proposals
#pragma once
//local headers
#include "tx_proposal.h"
//third party headers
#include <boost/multiprecision/cpp_int.hpp>
//standard headers
#include <cstddef>
//forward declarations
namespace carrot
{
struct CarrotSelectedInput
{
rct::xmr_amount amount;
crypto::key_image key_image;
};
static inline bool operator==(const CarrotSelectedInput &a, const CarrotSelectedInput &b)
{
return a.amount == b.amount && a.key_image == b.key_image;
}
static inline bool operator!=(const CarrotSelectedInput &a, const CarrotSelectedInput &b)
{
return !(a == b);
}
using select_inputs_func_t = std::function<void(
const boost::multiprecision::uint128_t&, // nominal output sum, w/o fee
const std::map<std::size_t, rct::xmr_amount>&, // absolute fee per input count
const std::size_t, // number of normal payment proposals
const std::size_t, // number of self-send payment proposals
std::vector<CarrotSelectedInput>& // selected inputs result
)>;
using carve_fees_and_balance_func_t = std::function<void(
const boost::multiprecision::uint128_t&, // input sum amount
const rct::xmr_amount, // fee
std::vector<CarrotPaymentProposalV1>&, // normal payment proposals [inout]
std::vector<CarrotPaymentProposalVerifiableSelfSendV1>& // selfsend payment proposals [inout]
)>;
std::uint64_t get_carrot_default_tx_extra_size(const std::size_t n_outputs);
static inline std::size_t get_fcmppp_tx_weight(const std::size_t num_inputs,
const std::size_t num_outputs,
const std::size_t tx_extra_size)
{
// @TODO: actually implement
return 200 + num_inputs * 1000 + num_outputs * 100 + tx_extra_size;
}
static size_t estimate_rct_tx_size_carrot(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
size_t size = 0;
// tx prefix
// first few bytes
size += 1 + 6;
// vin
size += n_inputs * (1+1+4+(mixin+1)*2+32);
// vout
size += n_outputs * (1+1+32+4+3+16);
// extra
size += extra_size;
// type
size += 1;
// amount_burnt
size += 8;
// return_address data
// NOTE: this will be wrong by 21 bytes for STAKE TXs using `protocol_tx_data_t`
size += n_outputs * (32 + 1);
// rct signatures
// rangeSigs
if (bulletproof || bulletproof_plus)
{
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
size += (2 * (6 + log_padded_outputs) + (bulletproof_plus ? 6 : (4 + 5))) * 32 + 3;
}
else
size += (2*64*32+32+64*32) * n_outputs;
// MGs/CLSAGs
if (clsag)
size += n_inputs * (64 * (mixin+1) + 64);
else
size += n_inputs * (64 * (mixin+1) + 32);
if (use_view_tags)
size += n_outputs * sizeof(crypto::view_tag);
// mixRing - not serialized, can be reconstructed
/* size += 2 * 32 * (mixin+1) * n_inputs; */
// pseudoOuts
size += 32 * n_inputs;
// ecdhInfo
size += 8 * n_outputs;
// outPk - only commitment is saved
size += 32 * n_outputs;
// txnFee
size += 4;
// p_r
size += 32;
// 2 zk proof
size += (2 * 96);
LOG_PRINT_L2("estimated " << (bulletproof_plus ? "bulletproof plus" : bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size;
}
static size_t estimate_tx_size_carrot(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
return estimate_rct_tx_size_carrot(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
}
static uint64_t estimate_tx_weigh_carrot(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags)
{
size_t size = estimate_tx_size_carrot(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
if ((bulletproof || bulletproof_plus) && n_outputs > 2)
{
const uint64_t bp_base = (32 * ((bulletproof_plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
const uint64_t bp_size = 32 * ((bulletproof_plus ? 6 : 9) + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
}
return size;
}
static uint64_t calculate_fee_from_weight_carrot(uint64_t base_fee, uint64_t weight, uint64_t fee_quantization_mask)
{
uint64_t fee = weight * base_fee;
fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
return fee;
}
static uint64_t estimate_fee_carrot(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask)
{
const size_t estimated_tx_weight = estimate_tx_weigh_carrot(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag, bulletproof_plus, use_view_tags);
return calculate_fee_from_weight_carrot(base_fee, estimated_tx_weight, fee_quantization_mask);
}
void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const std::vector<CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
const rct::xmr_amount fee_per_weight,
const rct::xmr_amount fee_quantization_mask,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
select_inputs_func_t &&select_inputs,
carve_fees_and_balance_func_t &&carve_fees_and_balance,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
CarrotTransactionProposalV1 &tx_proposal_out);
void make_carrot_transaction_proposal_v1_transfer(
const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const std::vector<CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
const rct::xmr_amount fee_per_weight,
const rct::xmr_amount fee_quantization_mask,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
select_inputs_func_t &&select_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
const std::set<std::size_t> &subtractable_normal_payment_proposals,
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
CarrotTransactionProposalV1 &tx_proposal_out);
void make_carrot_transaction_proposal_v1_sweep(
const std::vector<CarrotPaymentProposalV1> &normal_payment_proposals,
const std::vector<CarrotPaymentProposalVerifiableSelfSendV1> &selfsend_payment_proposals,
const rct::xmr_amount fee_per_weight,
const rct::xmr_amount fee_quantization_mask,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
std::vector<CarrotSelectedInput> &&selected_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
CarrotTransactionProposalV1 &tx_proposal_out);
} //namespace carrot