Skip to content

Commit 0518e35

Browse files
author
SyntheticBird45
committed
Applied edits
1 parent 6b58721 commit 0518e35

File tree

3 files changed

+37
-29
lines changed

3 files changed

+37
-29
lines changed

helper/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/consensus"
1010

1111
[features]
1212
# All features off by default.
13-
default = ["std"]
13+
default = []
1414
std = []
1515
atomic = ["dep:crossbeam"]
1616
asynch = ["dep:futures", "dep:rayon"]
1717
cast = []
1818
constants = []
19-
crypto = ["dep:curve25519-dalek", "dep:monero-serai"]
19+
crypto = ["dep:curve25519-dalek", "dep:monero-serai", "std"]
2020
fs = ["dep:dirs"]
2121
num = []
2222
map = ["cast", "dep:monero-serai", "dep:cuprate-constants"]

helper/src/crypto.rs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use curve25519_dalek::{
99
};
1010
use monero_serai::generators::H;
1111

12-
/// This is the decomposed amount table containing the mandatory Pre-RCT amounts. It is use to pre-compute
12+
/// This is the decomposed amount table containing the mandatory Pre-RCT amounts. It is used to pre-compute
1313
/// zero commitments at runtime.
1414
///
1515
/// Defined at:
@@ -38,13 +38,14 @@ const ZERO_COMMITMENT_DECOMPOSED_AMOUNT: [u64; 172] = [
3838
10000000000000000000
3939
];
4040

41-
/// Runtime initialized H generator.
41+
/// Runtime initialized [`H`] generator.
4242
static H_PRECOMP: LazyLock<VartimeEdwardsPrecomputation> =
4343
LazyLock::new(|| VartimeEdwardsPrecomputation::new([*H, ED25519_BASEPOINT_POINT]));
4444

4545
/// Runtime initialized zero commitment lookup table
4646
///
47-
/// ASSUMPTION: This function assume that the [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`]
47+
/// # Invariant
48+
/// This function assumes that the [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`]
4849
/// table is sorted.
4950
pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[(u64, EdwardsPoint); 172]> =
5051
LazyLock::new(|| {
@@ -60,34 +61,41 @@ pub static ZERO_COMMITMENT_LOOKUP_TABLE: LazyLock<[(u64, EdwardsPoint); 172]> =
6061
lookup_table
6162
});
6263

63-
/// This function compute the zero commitment given a specific amount.
64+
/// This function computes the zero commitment given a specific amount.
6465
///
6566
/// It will first attempt to lookup into the table of known Pre-RCT value.
6667
/// Compute it otherwise.
68+
#[expect(clippy::cast_possible_truncation)]
6769
pub fn compute_zero_commitments(amount: u64) -> EdwardsPoint {
68-
// OPTIMIZATION: We first make an arithmetic check that the value lies between valid pre-computed amounts. This permit to avoid the lookup cost if possible
69-
if amount > 10 && amount % 10 != 0 {
70-
return H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::from(1_u8)]);
70+
71+
if amount == 0 {
72+
return H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::from(1_u8)])
7173
}
74+
75+
// OPTIMIZATION: Unlike monerod which execute a linear search across its lookup
76+
// table (O(n)). Cuprate is making use of an arithmetic based constant time
77+
// version (O(1)). It has been benchmarked in both hit and miss scenarios against
78+
// a binary search lookup (O(log2(n))). To understand the following algorithm it
79+
// is important to observe the pattern that follows the values of
80+
// [`ZERO_COMMITMENT_DECOMPOSED_AMOUNT`].
81+
82+
// First obtain the logarithm base 10 of the amount. and extend it back to obtain
83+
// the amount without its most significant digit.
84+
let log = amount.checked_ilog10().unwrap();
85+
let div = 10_u64.pow(log);
7286

73-
// Binary search in lookup table (O(log2(n)) instead of O(n))
74-
let (mut start, mut end): (usize, usize) = (0, (ZERO_COMMITMENT_LOOKUP_TABLE.len() - 1));
75-
while start <= end {
76-
let mid = (start + end) / 2;
77-
let lookup = ZERO_COMMITMENT_LOOKUP_TABLE[mid].0;
78-
match amount {
79-
a if a == lookup => {
80-
return ZERO_COMMITMENT_LOOKUP_TABLE[mid].1;
81-
}
82-
a if a < lookup => {
83-
end = mid - 1;
84-
}
85-
_ => {
86-
start = mid + 1;
87-
}
88-
}
89-
}
87+
// Extract the most significant digit.
88+
let most_significant_digit = amount / div;
9089

91-
// Compute the zero commitment if not found.
92-
H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::from(1_u8)])
90+
// If the *rounded* version is different than the exact amount. Then
91+
// there aren't only trailing zeroes behind the most significant digit.
92+
// The amount is not part of the table and can calculated apart.
93+
if most_significant_digit * div != amount {
94+
return H_PRECOMP.vartime_multiscalar_mul([Scalar::from(amount), Scalar::ONE]);
95+
}
96+
97+
// Calculating the index back by progressing within the powers of 10.
98+
let index = (most_significant_digit + u64::from(log) * 10 - (u64::from(log) + 1)) as usize;
99+
100+
ZERO_COMMITMENT_LOOKUP_TABLE[index].1
93101
}

helper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod time;
3131
#[cfg(feature = "tx")]
3232
pub mod tx;
3333

34-
#[cfg(all(feature = "crypto", feature = "std"))]
34+
#[cfg(feature = "crypto")]
3535
pub mod crypto;
3636
//---------------------------------------------------------------------------------------------------- Private Usage
3737

0 commit comments

Comments
 (0)