tape_api/
utils.rs

1use steel::*;
2use crate::consts::*;
3use crate::error::*;
4use crate::types::*;
5use brine_tree::{MerkleTree, Leaf};
6
7/// Helper: check a condition is true and return an error if not
8pub fn check_condition<E>(condition: bool, err: E) -> ProgramResult
9where
10    E: Into<ProgramError>,
11{
12    if !condition {
13        return Err(err.into());
14    }
15    Ok(())
16}
17
18/// Helper: convert a slice to a fixed-size array padded with zeros
19pub fn padded_array<const N: usize>(input: &[u8]) -> [u8; N] {
20    assert!(input.len() <= N, "input too long");
21    let mut out = [0u8; N];
22    out[..input.len()].copy_from_slice(input);
23    out
24}
25
26/// Helper: convert a name to a fixed-size array
27pub fn to_name(val: &str) -> [u8; MAX_NAME_LEN] {
28    assert!(val.len() <= MAX_NAME_LEN, "name too long");
29    padded_array::<MAX_NAME_LEN>(val.as_bytes())
30}
31
32/// Helper: convert a name to a string
33pub fn from_name(val: &[u8; MAX_NAME_LEN]) -> String {
34    let mut name_bytes = val.to_vec();
35    name_bytes.retain(|&x| x != 0);
36    String::from_utf8(name_bytes).unwrap()
37}
38
39/// Helper: compute a leaf from a segment id, chunk id, and chunk
40#[inline(always)]
41pub fn compute_leaf(
42    segment_id: u64, 
43    chunk_id: u64, 
44    chunk: &Chunk
45) -> Leaf {
46    let segment_id = segment_id.to_le_bytes();
47    let chunk_id = chunk_id.to_le_bytes();
48
49    Leaf::new(&[
50        segment_id.as_ref(), // u64 (8 bytes)
51        chunk_id.as_ref(),   // u64 (8 bytes)
52        chunk.as_bytes(),
53    ])
54
55}
56
57/// Helper: write chunks to the Merkle tree
58#[inline(always)]
59pub fn write_chunks(
60    tree: &mut MerkleTree<{TREE_HEIGHT}>,
61    segment_id: u64,
62    segment: &Segment,
63) -> ProgramResult {
64    let chunks = segment.chunks();
65    for (chunk_id, chunk) in chunks.enumerate() {
66
67        let leaf = compute_leaf(
68            segment_id, 
69            chunk_id as u64, 
70            &chunk);
71
72        check_condition(
73            tree.try_add_leaf(leaf).is_ok(),
74            TapeError::WriteFailed,
75        )?;
76    }
77
78    Ok(())
79}
80
81// Helper: compute the recall tape number from a given challenge
82#[inline(always)]
83pub fn compute_recall_tape(
84    challenge: &[u8; 32],
85    total_tapes: u64,
86) -> u64 {
87    // Prevent division by zero
88    if total_tapes == 0 {
89        return 1;
90    }
91
92    // Compute the tape number from the challenge, tape 0 
93    // is invalid and reprseents no tape
94    (u64::from_le_bytes(challenge[0..8].try_into().unwrap()) % total_tapes)
95        .max(1)
96}
97
98// Helper: compute the recall segment number from a given challenge
99#[inline(always)]
100pub fn compute_recall_segment(
101    challenge: &[u8; 32],
102    total_segments: u64,
103) -> u64 {
104    // Prevent division by zero
105    if total_segments == 0 {
106        return 0;
107    }
108
109    u64::from_le_bytes(challenge[8..16].try_into().unwrap()) % total_segments
110}
111
112// Helper: compute the recall chunk number from a given challenge
113#[inline(always)]
114pub fn compute_recall_chunk(
115    challenge: &[u8; 32],
116) -> u64 {
117    u64::from_le_bytes(challenge[16..24].try_into().unwrap()) % MAGIC_NUMBER as u64
118}