mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Respond to 1.1 A2 (also cited as 2 1)
`read_vec` was unbounded. It now accepts an optional bound. In some places, we are able to define and provide a bound (Bulletproofs(+)' `L` and `R` vectors). In others, we cannot (the amount of inputs within a transaction, which is not subject to any rule in the current consensus other than the total transaction size limit). Usage of `None` in those locations preserves the existing behavior.
This commit is contained in:
@@ -51,8 +51,6 @@ pub fn H_pow_2() -> &'static [EdwardsPoint; 64] {
|
||||
pub const MAX_COMMITMENTS: usize = 16;
|
||||
/// The amount of bits a value within a commitment may use.
|
||||
pub const COMMITMENT_BITS: usize = 64;
|
||||
/// The logarithm (over 2) of the amount of bits a value within a commitment may use.
|
||||
pub const LOG_COMMITMENT_BITS: usize = 6; // 2 ** 6 == N
|
||||
|
||||
/// Container struct for Bulletproofs(+) generators.
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
@@ -214,6 +214,20 @@ pub fn read_array<R: Read, T: Debug, F: Fn(&mut R) -> io::Result<T>, const N: us
|
||||
}
|
||||
|
||||
/// Read a length-prefixed variable-length list of elements.
|
||||
pub fn read_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(f: F, r: &mut R) -> io::Result<Vec<T>> {
|
||||
read_raw_vec(f, read_varint(r)?, r)
|
||||
///
|
||||
/// An optional bound on the length of the result may be provided. If `None`, the returned `Vec`
|
||||
/// will be of the length read off the reader, if successfully read. If `Some(_)`, an error will be
|
||||
/// raised if the length read off the read is greater than the bound.
|
||||
pub fn read_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(
|
||||
f: F,
|
||||
length_bound: Option<usize>,
|
||||
r: &mut R,
|
||||
) -> io::Result<Vec<T>> {
|
||||
let declared_length: usize = read_varint(r)?;
|
||||
if let Some(length_bound) = length_bound {
|
||||
if declared_length > length_bound {
|
||||
Err(io::Error::other("vector exceeds bound on length"))?;
|
||||
}
|
||||
}
|
||||
read_raw_vec(f, declared_length, r)
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ impl Decoys {
|
||||
pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> {
|
||||
write_vec(write_varint, &self.offsets, w)?;
|
||||
w.write_all(&[self.signer_index])?;
|
||||
write_vec(
|
||||
write_raw_vec(
|
||||
|pair, w| {
|
||||
write_point(&pair[0], w)?;
|
||||
write_point(&pair[1], w)
|
||||
@@ -239,10 +239,12 @@ impl Decoys {
|
||||
/// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
|
||||
/// defined serialization.
|
||||
pub fn read(r: &mut impl io::Read) -> io::Result<Decoys> {
|
||||
let offsets = read_vec(read_varint, None, r)?;
|
||||
let len = offsets.len();
|
||||
Decoys::new(
|
||||
read_vec(read_varint, r)?,
|
||||
offsets,
|
||||
read_byte(r)?,
|
||||
read_vec(|r| Ok([read_point(r)?, read_point(r)?]), r)?,
|
||||
read_raw_vec(|r| Ok([read_point(r)?, read_point(r)?]), len, r)?,
|
||||
)
|
||||
.ok_or_else(|| io::Error::other("invalid Decoys"))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use curve25519_dalek::{
|
||||
edwards::EdwardsPoint,
|
||||
};
|
||||
|
||||
pub(crate) use monero_generators::{MAX_COMMITMENTS, COMMITMENT_BITS, LOG_COMMITMENT_BITS};
|
||||
pub(crate) use monero_generators::{MAX_COMMITMENTS, COMMITMENT_BITS};
|
||||
|
||||
pub(crate) fn multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
|
||||
let mut buf_scalars = Vec::with_capacity(pairs.len());
|
||||
|
||||
@@ -17,13 +17,13 @@ use curve25519_dalek::edwards::EdwardsPoint;
|
||||
|
||||
use monero_io::*;
|
||||
pub use monero_generators::MAX_COMMITMENTS;
|
||||
use monero_generators::COMMITMENT_BITS;
|
||||
use monero_primitives::Commitment;
|
||||
|
||||
pub(crate) mod scalar_vector;
|
||||
pub(crate) mod point_vector;
|
||||
|
||||
pub(crate) mod core;
|
||||
use crate::core::LOG_COMMITMENT_BITS;
|
||||
|
||||
pub(crate) mod batch_verifier;
|
||||
use batch_verifier::{BulletproofsBatchVerifier, BulletproofsPlusBatchVerifier};
|
||||
@@ -44,6 +44,11 @@ use crate::plus::{
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// The logarithm (over 2) of the amount of bits a value within a commitment may use.
|
||||
const LOG_COMMITMENT_BITS: usize = COMMITMENT_BITS.ilog2() as usize;
|
||||
// The maximum length of L/R `Vec`s.
|
||||
const MAX_LR: usize = (MAX_COMMITMENTS.ilog2() as usize) + LOG_COMMITMENT_BITS;
|
||||
|
||||
/// An error from proving/verifying Bulletproofs(+).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
@@ -265,8 +270,8 @@ impl Bulletproof {
|
||||
tau_x: read_scalar(r)?,
|
||||
mu: read_scalar(r)?,
|
||||
ip: IpProof {
|
||||
L: read_vec(read_point, r)?,
|
||||
R: read_vec(read_point, r)?,
|
||||
L: read_vec(read_point, Some(MAX_LR), r)?,
|
||||
R: read_vec(read_point, Some(MAX_LR), r)?,
|
||||
a: read_scalar(r)?,
|
||||
b: read_scalar(r)?,
|
||||
},
|
||||
@@ -284,8 +289,8 @@ impl Bulletproof {
|
||||
r_answer: read_scalar(r)?,
|
||||
s_answer: read_scalar(r)?,
|
||||
delta_answer: read_scalar(r)?,
|
||||
L: read_vec(read_point, r)?.into_iter().collect(),
|
||||
R: read_vec(read_point, r)?.into_iter().collect(),
|
||||
L: read_vec(read_point, Some(MAX_LR), r)?.into_iter().collect(),
|
||||
R: read_vec(read_point, Some(MAX_LR), r)?.into_iter().collect(),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ impl Input {
|
||||
let amount = if amount == 0 { None } else { Some(amount) };
|
||||
Input::ToKey {
|
||||
amount,
|
||||
key_offsets: read_vec(read_varint, r)?,
|
||||
key_offsets: read_vec(read_varint, None, r)?,
|
||||
key_image: read_torsion_free_point(r)?,
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ impl TransactionPrefix {
|
||||
pub fn read<R: Read>(r: &mut R, version: u64) -> io::Result<TransactionPrefix> {
|
||||
let additional_timelock = Timelock::read(r)?;
|
||||
|
||||
let inputs = read_vec(|r| Input::read(r), r)?;
|
||||
let inputs = read_vec(|r| Input::read(r), None, r)?;
|
||||
if inputs.is_empty() {
|
||||
Err(io::Error::other("transaction had no inputs"))?;
|
||||
}
|
||||
@@ -250,10 +250,10 @@ impl TransactionPrefix {
|
||||
let mut prefix = TransactionPrefix {
|
||||
additional_timelock,
|
||||
inputs,
|
||||
outputs: read_vec(|r| Output::read((!is_miner_tx) && (version == 2), r), r)?,
|
||||
outputs: read_vec(|r| Output::read((!is_miner_tx) && (version == 2), r), None, r)?,
|
||||
extra: vec![],
|
||||
};
|
||||
prefix.extra = read_vec(read_byte, r)?;
|
||||
prefix.extra = read_vec(read_byte, None, r)?;
|
||||
Ok(prefix)
|
||||
}
|
||||
|
||||
|
||||
@@ -181,16 +181,10 @@ impl ExtraField {
|
||||
size
|
||||
}),
|
||||
1 => ExtraField::PublicKey(read_point(r)?),
|
||||
2 => ExtraField::Nonce({
|
||||
let nonce = read_vec(read_byte, r)?;
|
||||
if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE {
|
||||
Err(io::Error::other("too long nonce"))?;
|
||||
}
|
||||
nonce
|
||||
}),
|
||||
2 => ExtraField::Nonce(read_vec(read_byte, Some(MAX_TX_EXTRA_NONCE_SIZE), r)?),
|
||||
3 => ExtraField::MergeMining(read_varint(r)?, read_bytes(r)?),
|
||||
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
|
||||
0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, r)?),
|
||||
4 => ExtraField::PublicKeys(read_vec(read_point, None, r)?),
|
||||
0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, None, r)?),
|
||||
_ => Err(io::Error::other("unknown extra field"))?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ impl SignableTransaction {
|
||||
/// defined serialization.
|
||||
pub fn read<R: io::Read>(r: &mut R) -> io::Result<SignableTransaction> {
|
||||
fn read_address<R: io::Read>(r: &mut R) -> io::Result<MoneroAddress> {
|
||||
String::from_utf8(read_vec(read_byte, r)?)
|
||||
String::from_utf8(read_vec(read_byte, None, r)?)
|
||||
.ok()
|
||||
.and_then(|str| MoneroAddress::from_str_with_unchecked_network(&str).ok())
|
||||
.ok_or_else(|| io::Error::other("invalid address"))
|
||||
@@ -484,9 +484,9 @@ impl SignableTransaction {
|
||||
rct_type: RctType::try_from(read_byte(r)?)
|
||||
.map_err(|()| io::Error::other("unsupported/invalid RctType"))?,
|
||||
outgoing_view_key: Zeroizing::new(read_bytes(r)?),
|
||||
inputs: read_vec(OutputWithDecoys::read, r)?,
|
||||
payments: read_vec(read_payment, r)?,
|
||||
data: read_vec(|r| read_vec(read_byte, r), r)?,
|
||||
inputs: read_vec(OutputWithDecoys::read, None, r)?,
|
||||
payments: read_vec(read_payment, None, r)?,
|
||||
data: read_vec(|r| read_vec(read_byte, None, r), None, r)?,
|
||||
fee_rate: FeeRate::read(r)?,
|
||||
};
|
||||
match res.validate() {
|
||||
|
||||
Reference in New Issue
Block a user