use std::collections::HashMap;
use triton_vm::prelude::*;
use triton_vm::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator;
use crate::arithmetic;
use crate::prelude::*;
use crate::traits::basic_snippet::Reviewer;
use crate::traits::basic_snippet::SignOffFingerprint;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct BagPeaks;
impl BagPeaks {
const INCONSISTENT_NUM_PEAKS_ERROR_ID: usize = 560;
}
impl BasicSnippet for BagPeaks {
fn parameters(&self) -> Vec<(DataType, String)> {
let mmr_accumulator = DataType::List(Box::new(DataType::Digest));
vec![(mmr_accumulator, "*mmra".to_string())]
}
fn return_values(&self) -> Vec<(DataType, String)> {
vec![(DataType::Digest, "digest".to_owned())]
}
fn entrypoint(&self) -> String {
"tasmlib_mmr_bag_peaks".to_string()
}
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let entrypoint = self.entrypoint();
let bagging_loop = format!("{entrypoint}_loop");
let destructure_mmra = MmrAccumulator::destructure();
let pop_count = library.import(Box::new(arithmetic::u64::popcount::PopCount));
triton_asm!(
{entrypoint}:
{&destructure_mmra}
addi 1 read_mem 2 pop 1
hint leaf_count : u64 = stack[0..2]
dup 1 dup 1
call {pop_count}
dup 3 read_mem 1 pop 1
dup 1 eq
assert error_id {Self::INCONSISTENT_NUM_PEAKS_ERROR_ID}
hint num_peaks: u32 = stack[0]
place 2
push 0
push 0
push 0
push 0
push 0
push 0
push 0
push 0
pick 9
pick 9
hash
pick 5
push {Digest::LEN}
mul
dup 6
add
place 5
dup 6 dup 6 eq push 0 eq
skiz call {bagging_loop}
pick 6 pick 6 pop 2
return
{bagging_loop}:
pick 5
read_mem {Digest::LEN}
place 10
hash
recurse_or_return
)
}
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
[].into()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use triton_vm::twenty_first::prelude::Mmr;
use twenty_first::math::other::random_elements;
use super::*;
use crate::test_prelude::*;
impl BagPeaks {
fn set_up_initial_state(&self, leaf_count: u64) -> FunctionInitialState {
let address = rand::random();
let mut stack = self.init_stack_for_isolated_run();
push_encodable(&mut stack, &address);
let mut memory = HashMap::new();
let num_peaks = leaf_count.count_ones();
let mmra =
MmrAccumulator::init(random_elements::<Digest>(num_peaks as usize), leaf_count);
encode_to_memory(&mut memory, address, &mmra);
FunctionInitialState { stack, memory }
}
}
impl Function for BagPeaks {
fn rust_shadow(
&self,
stack: &mut Vec<BFieldElement>,
memory: &mut HashMap<BFieldElement, BFieldElement>,
) {
let address = pop_encodable(stack);
let mmra = *MmrAccumulator::decode_from_memory(memory, address).unwrap();
fn bag_peaks(peaks: &[Digest], leaf_count: u64) -> Digest {
let [lo_limb, hi_limb] = leaf_count.encode()[..] else {
panic!("internal error: unknown encoding of type `u64`")
};
let padded_leaf_count = bfe_array![lo_limb, hi_limb, 0, 0, 0, 0, 0, 0, 0, 0];
let hashed_leaf_count = Digest::new(Tip5::hash_10(&padded_leaf_count));
peaks
.iter()
.rev()
.fold(hashed_leaf_count, |acc, &peak| Tip5::hash_pair(peak, acc))
}
let bag = bag_peaks(&mmra.peaks(), mmra.num_leafs());
println!("bag: {bag}");
push_encodable(stack, &bag);
}
fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
bench_case: Option<BenchmarkCase>,
) -> FunctionInitialState {
let num_leafs = match bench_case {
Some(BenchmarkCase::CommonCase) => 348753,
Some(BenchmarkCase::WorstCase) => 843759843768,
None => StdRng::from_seed(seed).random_range(0u64..(u64::MAX >> 1)),
};
self.set_up_initial_state(num_leafs)
}
fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
(0..=5)
.chain([63])
.map(|num_peaks| self.set_up_initial_state((1 << num_peaks) - 1))
.collect()
}
}
#[test]
fn rust_shadow() {
ShadowedFunction::new(BagPeaks).test()
}
}
#[cfg(test)]
mod benches {
use super::BagPeaks;
use crate::test_prelude::*;
#[test]
fn benchmark() {
ShadowedFunction::new(BagPeaks).bench();
}
}