maili_consensus/deposit/
source.rs

1//! Classification of deposit transaction source
2
3use alloc::string::String;
4use alloy_primitives::{keccak256, B256};
5
6/// Source domain identifiers for deposit transactions.
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
8#[repr(u8)]
9pub enum DepositSourceDomainIdentifier {
10    /// A user deposit source.
11    User = 0,
12    /// A L1 info deposit source.
13    L1Info = 1,
14    /// An upgrade deposit source.
15    Upgrade = 2,
16    /// Deposit context closing transaction.
17    DepositContext = 3,
18}
19
20/// Source domains for deposit transactions.
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub enum DepositSourceDomain {
23    /// A user deposit source.
24    User(UserDepositSource),
25    /// A L1 info deposit source.
26    L1Info(L1InfoDepositSource),
27    /// An upgrade deposit source.
28    Upgrade(UpgradeDepositSource),
29    /// A deposit context closing source
30    DepositContext(DepositContextDepositSource),
31}
32
33impl DepositSourceDomain {
34    /// Returns the source hash.
35    pub fn source_hash(&self) -> B256 {
36        match self {
37            Self::User(ds) => ds.source_hash(),
38            Self::L1Info(ds) => ds.source_hash(),
39            Self::Upgrade(ds) => ds.source_hash(),
40            Self::DepositContext(ds) => ds.source_hash(),
41        }
42    }
43}
44
45/// A deposit transaction source.
46#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
47pub struct UserDepositSource {
48    /// The L1 block hash.
49    pub l1_block_hash: B256,
50    /// The log index.
51    pub log_index: u64,
52}
53
54impl UserDepositSource {
55    /// Creates a new [UserDepositSource].
56    pub const fn new(l1_block_hash: B256, log_index: u64) -> Self {
57        Self { l1_block_hash, log_index }
58    }
59
60    /// Returns the source hash.
61    pub fn source_hash(&self) -> B256 {
62        let mut input = [0u8; 32 * 2];
63        input[..32].copy_from_slice(&self.l1_block_hash[..]);
64        input[32 * 2 - 8..].copy_from_slice(&self.log_index.to_be_bytes());
65        let deposit_id_hash = keccak256(input);
66        let mut domain_input = [0u8; 32 * 2];
67        let identifier_bytes: [u8; 8] = (DepositSourceDomainIdentifier::User as u64).to_be_bytes();
68        domain_input[32 - 8..32].copy_from_slice(&identifier_bytes);
69        domain_input[32..].copy_from_slice(&deposit_id_hash[..]);
70        keccak256(domain_input)
71    }
72}
73
74/// A L1 info deposit transaction source.
75#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
76pub struct L1InfoDepositSource {
77    /// The L1 block hash.
78    pub l1_block_hash: B256,
79    /// The sequence number.
80    pub seq_number: u64,
81}
82
83impl L1InfoDepositSource {
84    /// Creates a new [L1InfoDepositSource].
85    pub const fn new(l1_block_hash: B256, seq_number: u64) -> Self {
86        Self { l1_block_hash, seq_number }
87    }
88
89    /// Returns the source hash.
90    pub fn source_hash(&self) -> B256 {
91        let mut input = [0u8; 32 * 2];
92        input[..32].copy_from_slice(&self.l1_block_hash[..]);
93        input[32 * 2 - 8..].copy_from_slice(&self.seq_number.to_be_bytes());
94        let deposit_id_hash = keccak256(input);
95        let mut domain_input = [0u8; 32 * 2];
96        let identifier_bytes: [u8; 8] =
97            (DepositSourceDomainIdentifier::L1Info as u64).to_be_bytes();
98        domain_input[32 - 8..32].copy_from_slice(&identifier_bytes);
99        domain_input[32..].copy_from_slice(&deposit_id_hash[..]);
100        keccak256(domain_input)
101    }
102}
103
104/// An upgrade deposit transaction source.
105///
106/// This implements the translation of upgrade-tx identity information to a deposit source-hash,
107/// which makes the deposit uniquely identifiable.
108/// System-upgrade transactions have their own domain for source-hashes,
109/// to not conflict with user-deposits or deposited L1 information.
110/// The intent identifies the upgrade-tx uniquely, in a human-readable way.
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub struct UpgradeDepositSource {
113    /// The intent.
114    pub intent: String,
115}
116
117impl UpgradeDepositSource {
118    /// Creates a new [UpgradeDepositSource].
119    pub const fn new(intent: String) -> Self {
120        Self { intent }
121    }
122
123    /// Returns the source hash.
124    pub fn source_hash(&self) -> B256 {
125        let intent_hash = keccak256(self.intent.as_bytes());
126        let mut domain_input = [0u8; 32 * 2];
127        let identifier_bytes: [u8; 8] =
128            (DepositSourceDomainIdentifier::Upgrade as u64).to_be_bytes();
129        domain_input[32 - 8..32].copy_from_slice(&identifier_bytes);
130        domain_input[32..].copy_from_slice(&intent_hash[..]);
131        keccak256(domain_input)
132    }
133}
134
135/// A deposit context transaction source.
136#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
137pub struct DepositContextDepositSource {
138    /// The L1 block hash.
139    pub l1_block_hash: B256,
140    /// The sequence number.
141    pub seq_number: u64,
142}
143
144impl DepositContextDepositSource {
145    /// Creates a new [L1InfoDepositSource].
146    pub const fn new(l1_block_hash: B256, seq_number: u64) -> Self {
147        Self { l1_block_hash, seq_number }
148    }
149
150    /// Returns the source hash.
151    pub fn source_hash(&self) -> B256 {
152        let mut input = [0u8; 32 * 2];
153        input[..32].copy_from_slice(&self.l1_block_hash[..]);
154        input[32 * 2 - 8..].copy_from_slice(&self.seq_number.to_be_bytes());
155        let deposit_id_hash = keccak256(input);
156        let mut domain_input = [0u8; 32 * 2];
157        let identifier_bytes: [u8; 8] =
158            (DepositSourceDomainIdentifier::DepositContext as u64).to_be_bytes();
159        domain_input[32 - 8..32].copy_from_slice(&identifier_bytes);
160        domain_input[32..].copy_from_slice(&deposit_id_hash[..]);
161        keccak256(domain_input)
162    }
163}