Skip to main content

miden_agglayer/
agglayer_note.rs

1use miden_protocol::note::{NoteScript, NoteScriptRoot};
2use miden_standards::note::costs::NoteCost;
3
4use crate::{
5    B2AggNote,
6    ClaimNote,
7    ConfigAggBridgeNote,
8    DeregisterAggFaucetNote,
9    RemoveGerNote,
10    UpdateGerNote,
11};
12
13// AGGLAYER NOTE
14// ================================================================================================
15
16/// The enum holding the types of notes provided by `miden-agglayer`, mirroring
17/// [`StandardNote`](miden_standards::note::StandardNote).
18#[allow(non_camel_case_types)]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum AgglayerNote {
21    CLAIM,
22    B2AGG,
23    CONFIG_AGG_BRIDGE,
24    DEREGISTER_AGG_FAUCET,
25    UPDATE_GER,
26    REMOVE_GER,
27}
28
29impl AgglayerNote {
30    // CONSTRUCTOR
31    // --------------------------------------------------------------------------------------------
32
33    /// Returns an [`AgglayerNote`] instance based on the provided script root. Returns `None`
34    /// if the provided root does not match any agglayer note script.
35    pub fn from_script_root(root: NoteScriptRoot) -> Option<Self> {
36        match root {
37            r if r == ClaimNote::script_root() => Some(Self::CLAIM),
38            r if r == B2AggNote::script_root() => Some(Self::B2AGG),
39            r if r == ConfigAggBridgeNote::script_root() => Some(Self::CONFIG_AGG_BRIDGE),
40            r if r == DeregisterAggFaucetNote::script_root() => Some(Self::DEREGISTER_AGG_FAUCET),
41            r if r == UpdateGerNote::script_root() => Some(Self::UPDATE_GER),
42            r if r == RemoveGerNote::script_root() => Some(Self::REMOVE_GER),
43            _ => None,
44        }
45    }
46
47    // PUBLIC ACCESSORS
48    // --------------------------------------------------------------------------------------------
49
50    /// Returns the name of this [`AgglayerNote`] variant as a string.
51    pub fn name(&self) -> &'static str {
52        match self {
53            Self::CLAIM => "CLAIM",
54            Self::B2AGG => "B2AGG",
55            Self::CONFIG_AGG_BRIDGE => "CONFIG_AGG_BRIDGE",
56            Self::DEREGISTER_AGG_FAUCET => "DEREGISTER_AGG_FAUCET",
57            Self::UPDATE_GER => "UPDATE_GER",
58            Self::REMOVE_GER => "REMOVE_GER",
59        }
60    }
61
62    /// Returns the note script of the current [`AgglayerNote`] instance.
63    pub fn script(&self) -> NoteScript {
64        match self {
65            Self::CLAIM => ClaimNote::script(),
66            Self::B2AGG => B2AggNote::script(),
67            Self::CONFIG_AGG_BRIDGE => ConfigAggBridgeNote::script(),
68            Self::DEREGISTER_AGG_FAUCET => DeregisterAggFaucetNote::script(),
69            Self::UPDATE_GER => UpdateGerNote::script(),
70            Self::REMOVE_GER => RemoveGerNote::script(),
71        }
72    }
73
74    /// Returns the script root of the current [`AgglayerNote`] instance.
75    pub fn script_root(&self) -> NoteScriptRoot {
76        match self {
77            Self::CLAIM => ClaimNote::script_root(),
78            Self::B2AGG => B2AggNote::script_root(),
79            Self::CONFIG_AGG_BRIDGE => ConfigAggBridgeNote::script_root(),
80            Self::DEREGISTER_AGG_FAUCET => DeregisterAggFaucetNote::script_root(),
81            Self::UPDATE_GER => UpdateGerNote::script_root(),
82            Self::REMOVE_GER => RemoveGerNote::script_root(),
83        }
84    }
85
86    /// Returns the benchmarked consumption cost of this note.
87    fn cost(&self) -> NoteCost {
88        match self {
89            Self::CLAIM => NoteCost::of::<ClaimNote>(),
90            Self::B2AGG => NoteCost::of::<B2AggNote>(),
91            Self::CONFIG_AGG_BRIDGE => NoteCost::of::<ConfigAggBridgeNote>(),
92            Self::DEREGISTER_AGG_FAUCET => NoteCost::of::<DeregisterAggFaucetNote>(),
93            Self::UPDATE_GER => NoteCost::of::<UpdateGerNote>(),
94            Self::REMOVE_GER => NoteCost::of::<RemoveGerNote>(),
95        }
96    }
97
98    /// Returns the benchmarked consumption cost of the agglayer note with the given script
99    /// root, or `None` if the root does not match an agglayer note.
100    ///
101    /// The `NetworkNotePricer` in `miden-tx` combines this lookup with the standard notes'
102    /// (`StandardNote::note_cost`) to resolve the cost of any priced note.
103    pub fn note_cost(root: NoteScriptRoot) -> Option<NoteCost> {
104        Some(Self::from_script_root(root)?.cost())
105    }
106}
107
108// TESTS
109// ================================================================================================
110
111#[cfg(test)]
112mod tests {
113    use miden_standards::note::{MintNote, P2idNote};
114
115    use super::*;
116    use crate::costs::{
117        B2AGG_CONSUMPTION_CYCLES,
118        CLAIM_CONSUMPTION_CYCLES,
119        CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES,
120        DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES,
121        REMOVE_GER_CONSUMPTION_CYCLES,
122        UPDATE_GER_CONSUMPTION_CYCLES,
123    };
124
125    const ALL_NOTES: [AgglayerNote; 6] = [
126        AgglayerNote::CLAIM,
127        AgglayerNote::B2AGG,
128        AgglayerNote::CONFIG_AGG_BRIDGE,
129        AgglayerNote::DEREGISTER_AGG_FAUCET,
130        AgglayerNote::UPDATE_GER,
131        AgglayerNote::REMOVE_GER,
132    ];
133
134    /// Ties the hand-written per-variant tables to each other and each variant's cost to its
135    /// own table constant.
136    #[test]
137    fn variant_tables_are_self_consistent_and_pin_the_table_constants() {
138        for note in ALL_NOTES {
139            assert_eq!(AgglayerNote::from_script_root(note.script_root()), Some(note));
140            assert_eq!(note.script().root(), note.script_root());
141
142            let expected_cycles = match note {
143                AgglayerNote::CLAIM => CLAIM_CONSUMPTION_CYCLES,
144                AgglayerNote::B2AGG => B2AGG_CONSUMPTION_CYCLES,
145                AgglayerNote::CONFIG_AGG_BRIDGE => CONFIG_AGG_BRIDGE_CONSUMPTION_CYCLES,
146                AgglayerNote::DEREGISTER_AGG_FAUCET => DEREGISTER_AGG_FAUCET_CONSUMPTION_CYCLES,
147                AgglayerNote::UPDATE_GER => UPDATE_GER_CONSUMPTION_CYCLES,
148                AgglayerNote::REMOVE_GER => REMOVE_GER_CONSUMPTION_CYCLES,
149            };
150            let cost = AgglayerNote::note_cost(note.script_root())
151                .expect("every agglayer note should have a cost");
152            assert_eq!(cost.cycles(), expected_cycles, "cost mismatch for {}", note.name());
153        }
154
155        // A standard-note root is not an agglayer note.
156        assert_eq!(AgglayerNote::from_script_root(P2idNote::script_root()), None);
157    }
158
159    #[test]
160    fn note_cost_resolves_only_agglayer_notes() {
161        let claim_cost =
162            AgglayerNote::note_cost(ClaimNote::script_root()).expect("CLAIM should have a cost");
163        assert_eq!(claim_cost.cycles(), CLAIM_CONSUMPTION_CYCLES);
164        assert_eq!(claim_cost.created_notes(), [MintNote::script_root()]);
165
166        assert!(AgglayerNote::note_cost(P2idNote::script_root()).is_none());
167    }
168}