tasm_lib/mmr/
leaf_index_to_mt_index_and_peak_index.rsuse std::collections::HashMap;
use triton_vm::prelude::*;
use crate::arithmetic::u64::add::Add;
use crate::arithmetic::u64::and::And;
use crate::arithmetic::u64::decr::Decr;
use crate::arithmetic::u64::log_2_floor::Log2Floor;
use crate::arithmetic::u64::lt_preserve_args::LtPreserveArgs;
use crate::arithmetic::u64::popcount::PopCount;
use crate::arithmetic::u64::pow2::Pow2;
use crate::prelude::*;
use crate::traits::basic_snippet::Reviewer;
use crate::traits::basic_snippet::SignOffFingerprint;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct MmrLeafIndexToMtIndexAndPeakIndex;
impl MmrLeafIndexToMtIndexAndPeakIndex {
pub const LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID: i128 = 350;
}
impl BasicSnippet for MmrLeafIndexToMtIndexAndPeakIndex {
fn inputs(&self) -> Vec<(DataType, String)> {
["num_leafs", "leaf_index"]
.map(|s| (DataType::U64, s.to_string()))
.to_vec()
}
fn outputs(&self) -> Vec<(DataType, String)> {
vec![
(DataType::U64, "mt_index".to_string()),
(DataType::U32, "peak_index".to_string()),
]
}
fn entrypoint(&self) -> String {
"tasmlib_mmr_leaf_index_to_mt_index_and_peak_index".to_string()
}
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let entrypoint = self.entrypoint();
let log_2_floor_u64 = library.import(Box::new(Log2Floor));
let lt_u64 = library.import(Box::new(LtPreserveArgs));
let add_u64 = library.import(Box::new(Add));
let and_u64 = library.import(Box::new(And));
let pow2_u64 = library.import(Box::new(Pow2));
let decr_u64 = library.import(Box::new(Decr));
let popcount_u64 = library.import(Box::new(PopCount));
triton_asm!(
{entrypoint}:
call {lt_u64}
assert error_id {Self::LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID}
dup 3
dup 2
xor
dup 3
dup 2
xor
call {log_2_floor_u64}
call {pow2_u64}
dup 1 dup 1
call {decr_u64}
dup 1 dup 1
pick 7 pick 7
call {and_u64}
pick 5 pick 5
call {add_u64}
place 5 place 5
dup 3 dup 3
call {popcount_u64}
place 4
call {and_u64}
call {popcount_u64}
push -1
mul
add
addi -1
return
)
}
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
let mut sign_offs = HashMap::new();
sign_offs.insert(Reviewer("ferdinand"), 0xdbc9fd10de0f2dbd.into());
sign_offs
}
}
#[cfg(test)]
pub(crate) mod tests {
use twenty_first::util_types::mmr::shared_basic::leaf_index_to_mt_index_and_peak_index;
use super::*;
use crate::test_prelude::*;
impl MmrLeafIndexToMtIndexAndPeakIndex {
pub fn assert_expected_behavior(&self, num_leafs: u64, leaf_index: u64) {
let initial_stack = self.set_up_test_stack((num_leafs, leaf_index));
let mut expected_stack = initial_stack.clone();
self.rust_shadow(&mut expected_stack);
test_rust_equivalence_given_complete_state(
&ShadowedClosure::new(Self),
&initial_stack,
&[],
&NonDeterminism::default(),
&None,
Some(&expected_stack),
);
}
}
impl Closure for MmrLeafIndexToMtIndexAndPeakIndex {
type Args = (u64, u64);
fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
let (num_leafs, leaf_index) = pop_encodable::<Self::Args>(stack);
let (mt_index, peak_index) =
leaf_index_to_mt_index_and_peak_index(leaf_index, num_leafs);
push_encodable(stack, &mt_index);
push_encodable(stack, &peak_index);
}
fn pseudorandom_args(
&self,
seed: [u8; 32],
bench_case: Option<BenchmarkCase>,
) -> Self::Args {
let Some(bench_case) = bench_case else {
let mut rng = StdRng::from_seed(seed);
let num_leafs = rng.random_range(1..1 << 63);
let leaf_index = rng.random_range(0..num_leafs);
return (num_leafs, leaf_index);
};
match bench_case {
BenchmarkCase::WorstCase => ((1 << 63) - 1, (1 << 63) - 63),
BenchmarkCase::CommonCase => ((1 << 32) - 1, (3 << 30) + 100_000),
}
}
fn corner_case_args(&self) -> Vec<Self::Args> {
let mut states = vec![];
for num_leafs in (1..=5).chain([14]).chain(32..=37) {
for leaf_index in 0..num_leafs {
states.push((num_leafs, leaf_index));
}
}
let more_states = (10..20)
.map(|pow| 1 << pow)
.flat_map(|n| [(14, n), (n + 9, n + 11), (n + 10, n + 11)])
.map(|(leaf_index, num_leafs)| (num_leafs, leaf_index));
states.extend(more_states);
states.push((1 << 31, 5_550_001));
states.push(((1 << 31) + (1 << 20), 5_550_001));
for num_leafs in [(1 << 31) + (1 << 20) - 1, (1 << 63) + (1 << 62) - 1] {
states.push((num_leafs, num_leafs - 1));
}
states
}
}
#[test]
fn rust_shadow() {
ShadowedClosure::new(MmrLeafIndexToMtIndexAndPeakIndex).test();
}
#[proptest]
fn property_test(
#[strategy(1_u64..)] num_leafs: u64,
#[strategy(0_u64..#num_leafs)] leaf_index: u64,
) {
MmrLeafIndexToMtIndexAndPeakIndex.assert_expected_behavior(num_leafs, leaf_index);
}
#[proptest]
fn negative_property_test(num_leafs: u64, #[strategy(#num_leafs..)] leaf_index: u64) {
let initial_stack =
MmrLeafIndexToMtIndexAndPeakIndex.set_up_test_stack((num_leafs, leaf_index));
test_assertion_failure(
&ShadowedClosure::new(MmrLeafIndexToMtIndexAndPeakIndex),
InitVmState::with_stack(initial_stack),
&[MmrLeafIndexToMtIndexAndPeakIndex::LEAF_INDEX_GE_NUM_LEAFS_ERROR_ID],
);
}
}
#[cfg(test)]
mod benches {
use super::*;
use crate::test_prelude::*;
#[test]
fn benchmark() {
ShadowedClosure::new(MmrLeafIndexToMtIndexAndPeakIndex).bench();
}
}