diff --git a/coins/monero/src/ringct/bulletproofs/mod.rs b/coins/monero/src/ringct/bulletproofs/mod.rs index 2d2ff9e8..6e7481a7 100644 --- a/coins/monero/src/ringct/bulletproofs/mod.rs +++ b/coins/monero/src/ringct/bulletproofs/mod.rs @@ -11,6 +11,7 @@ use crate::{Commitment, wallet::TransactionError, serialize::*}; pub(crate) mod scalar_vector; pub(crate) mod core; +use self::core::LOG_N; pub(crate) mod original; pub(crate) mod plus; @@ -28,19 +29,23 @@ pub enum Bulletproofs { } impl Bulletproofs { - // TODO - pub(crate) fn fee_weight(outputs: usize) -> usize { - let proofs = 6 + usize::try_from(usize::BITS - (outputs - 1).leading_zeros()).unwrap(); - let len = (9 + (2 * proofs)) * 32; + pub(crate) fn fee_weight(plus: bool, outputs: usize) -> usize { + let fields = if plus { 6 } else { 9 }; - let mut clawback = 0; - let padded = 1 << (proofs - 6); - if padded > 2 { - const BP_BASE: usize = 368; - clawback = ((BP_BASE * padded) - len) * 4 / 5; - } + #[allow(non_snake_case)] + let mut LR_len = usize::try_from(usize::BITS - (outputs - 1).leading_zeros()).unwrap(); + let padded_outputs = 1 << LR_len; + LR_len += LOG_N; - len + clawback + let len = (fields + (2 * LR_len)) * 32; + len + + if padded_outputs <= 2 { + 0 + } else { + let base = ((fields + (2 * (LOG_N + 1))) * 32) / 2; + let size = (fields + (2 * LR_len)) * 32; + ((base * padded_outputs) - size) * 4 / 5 + } } pub fn prove( diff --git a/coins/monero/src/ringct/mod.rs b/coins/monero/src/ringct/mod.rs index ea8abb1f..bd663b65 100644 --- a/coins/monero/src/ringct/mod.rs +++ b/coins/monero/src/ringct/mod.rs @@ -9,6 +9,7 @@ pub mod clsag; pub mod bulletproofs; use crate::{ + Protocol, serialize::*, ringct::{clsag::Clsag, bulletproofs::Bulletproofs}, }; @@ -86,8 +87,9 @@ impl RctPrunable { } } - pub(crate) fn fee_weight(ring_len: usize, inputs: usize, outputs: usize) -> usize { - 1 + Bulletproofs::fee_weight(outputs) + (inputs * (Clsag::fee_weight(ring_len) + 32)) + pub(crate) fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize { + 1 + Bulletproofs::fee_weight(protocol.bp_plus(), outputs) + + (inputs * (Clsag::fee_weight(protocol.ring_len()) + 32)) } pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { @@ -142,8 +144,8 @@ pub struct RctSignatures { } impl RctSignatures { - pub(crate) fn fee_weight(ring_len: usize, inputs: usize, outputs: usize) -> usize { - RctBase::fee_weight(outputs) + RctPrunable::fee_weight(ring_len, inputs, outputs) + pub(crate) fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize { + RctBase::fee_weight(outputs) + RctPrunable::fee_weight(protocol, inputs, outputs) } pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index 6a9adb6e..2fd8bade 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -371,8 +371,7 @@ impl Rpc { ) .await?; - // TODO: Support time based lock times. These shouldn't be needed, and it may be painful to - // get the median time for the given height, yet we do need to in order to be complete + // TODO: https://github.com/serai-dex/serai/issues/104 outs .outs .iter() diff --git a/coins/monero/src/transaction.rs b/coins/monero/src/transaction.rs index 83b50e48..9dcb3b36 100644 --- a/coins/monero/src/transaction.rs +++ b/coins/monero/src/transaction.rs @@ -5,7 +5,7 @@ use zeroize::Zeroize; use curve25519_dalek::edwards::EdwardsPoint; use crate::{ - hash, + Protocol, hash, serialize::*, ringct::{RctPrunable, RctSignatures}, }; @@ -193,9 +193,14 @@ pub struct Transaction { } impl Transaction { - pub(crate) fn fee_weight(ring_len: usize, inputs: usize, outputs: usize, extra: usize) -> usize { - TransactionPrefix::fee_weight(ring_len, inputs, outputs, extra) + - RctSignatures::fee_weight(ring_len, inputs, outputs) + pub(crate) fn fee_weight( + protocol: Protocol, + inputs: usize, + outputs: usize, + extra: usize, + ) -> usize { + TransactionPrefix::fee_weight(protocol.ring_len(), inputs, outputs, extra) + + RctSignatures::fee_weight(protocol, inputs, outputs) } pub fn serialize(&self, w: &mut W) -> std::io::Result<()> { diff --git a/coins/monero/src/wallet/send/mod.rs b/coins/monero/src/wallet/send/mod.rs index e3c0f614..af5b5e89 100644 --- a/coins/monero/src/wallet/send/mod.rs +++ b/coins/monero/src/wallet/send/mod.rs @@ -212,12 +212,8 @@ impl SignableTransaction { let extra = Extra::fee_weight(outputs); // Calculate the fee. - let mut fee = fee_rate.calculate(Transaction::fee_weight( - protocol.ring_len(), - inputs.len(), - outputs, - extra, - )); + let mut fee = + fee_rate.calculate(Transaction::fee_weight(protocol, inputs.len(), outputs, extra)); // Make sure we have enough funds let in_amount = inputs.iter().map(|input| input.commitment.amount).sum::(); @@ -229,12 +225,9 @@ impl SignableTransaction { // If we have yet to add a change output, do so if it's economically viable if (!change) && change_address.is_some() && (in_amount != out_amount) { // Check even with the new fee, there's remaining funds - let change_fee = fee_rate.calculate(Transaction::fee_weight( - protocol.ring_len(), - inputs.len(), - outputs + 1, - extra, - )) - fee; + let change_fee = + fee_rate.calculate(Transaction::fee_weight(protocol, inputs.len(), outputs + 1, extra)) - + fee; if (out_amount + change_fee) < in_amount { change = true; out_amount += change_fee;