spl_account_compression/events/
changelog_event.rs

1use crate::state::PathNode;
2
3use anchor_lang::prelude::*;
4use spl_concurrent_merkle_tree::changelog::ChangeLog;
5
6#[derive(AnchorDeserialize, AnchorSerialize)]
7#[repr(C)]
8pub enum ChangeLogEvent {
9    V1(ChangeLogEventV1),
10}
11
12#[derive(AnchorDeserialize, AnchorSerialize)]
13pub struct ChangeLogEventV1 {
14    /// Public key of the ConcurrentMerkleTree
15    pub id: Pubkey,
16
17    /// Nodes of off-chain merkle tree needed by indexer
18    pub path: Vec<PathNode>,
19
20    /// Index corresponding to the number of successful operations on this tree.
21    /// Used by the off-chain indexer to figure out when there are gaps to be backfilled.
22    pub seq: u64,
23
24    /// Bitmap of node parity (used when hashing)
25    pub index: u32,
26}
27
28impl ChangeLogEvent {
29    pub fn new(id: Pubkey, path: Vec<PathNode>, seq: u64, index: u32) -> Self {
30        Self::V1(ChangeLogEventV1 {
31            id,
32            path,
33            seq,
34            index,
35        })
36    }
37}
38
39impl<const MAX_DEPTH: usize> From<(Box<ChangeLog<MAX_DEPTH>>, Pubkey, u64)>
40    for Box<ChangeLogEvent>
41{
42    fn from(log_info: (Box<ChangeLog<MAX_DEPTH>>, Pubkey, u64)) -> Self {
43        let (changelog, tree_id, seq) = log_info;
44        let path_len = changelog.path.len() as u32;
45        let mut path: Vec<PathNode> = changelog
46            .path
47            .iter()
48            .enumerate()
49            .map(|(lvl, n)| {
50                PathNode::new(
51                    *n,
52                    (1 << (path_len - lvl as u32)) + (changelog.index >> lvl),
53                )
54            })
55            .collect();
56        path.push(PathNode::new(changelog.root, 1));
57        Box::new(ChangeLogEvent::V1(ChangeLogEventV1 {
58            id: tree_id,
59            path,
60            seq,
61            index: changelog.index,
62        }))
63    }
64}