aztec_ethereum/
messaging.rs

1//! L1↔L2 messaging types and utilities.
2//!
3//! Mirrors upstream:
4//! - `stdlib/src/messaging/l1_actor.ts`
5//! - `stdlib/src/messaging/l2_actor.ts`
6//! - `stdlib/src/messaging/l1_to_l2_message.ts`
7
8use aztec_core::types::{AztecAddress, EthAddress, Fr};
9use serde::{Deserialize, Serialize};
10
11// ---------------------------------------------------------------------------
12// L1Actor — sender on Ethereum
13// ---------------------------------------------------------------------------
14
15/// An actor on L1 (Ethereum), identified by an Eth address and chain ID.
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub struct L1Actor {
18    /// Ethereum address of the sender.
19    pub sender: EthAddress,
20    /// L1 chain ID.
21    pub chain_id: u64,
22}
23
24impl L1Actor {
25    pub fn new(sender: EthAddress, chain_id: u64) -> Self {
26        Self { sender, chain_id }
27    }
28
29    pub fn empty() -> Self {
30        Self {
31            sender: EthAddress::default(),
32            chain_id: 0,
33        }
34    }
35
36    /// Serialize to field elements: `[sender_as_field, chain_id_as_field]`.
37    pub fn to_fields(&self) -> [Fr; 2] {
38        let sender_fr = {
39            let mut bytes = [0u8; 32];
40            bytes[12..32].copy_from_slice(&self.sender.0);
41            Fr::from(bytes)
42        };
43        [sender_fr, Fr::from(self.chain_id)]
44    }
45}
46
47// ---------------------------------------------------------------------------
48// L2Actor — recipient on Aztec
49// ---------------------------------------------------------------------------
50
51/// An actor on L2 (Aztec), identified by an Aztec address and protocol version.
52#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53pub struct L2Actor {
54    /// Aztec address of the recipient.
55    pub recipient: AztecAddress,
56    /// Protocol version.
57    pub version: u64,
58}
59
60impl L2Actor {
61    pub fn new(recipient: AztecAddress, version: u64) -> Self {
62        Self { recipient, version }
63    }
64
65    pub fn empty() -> Self {
66        Self {
67            recipient: AztecAddress::zero(),
68            version: 0,
69        }
70    }
71
72    /// Serialize to field elements: `[recipient_as_field, version_as_field]`.
73    pub fn to_fields(&self) -> [Fr; 2] {
74        [Fr::from(self.recipient), Fr::from(self.version)]
75    }
76}
77
78// ---------------------------------------------------------------------------
79// L1ToL2Message
80// ---------------------------------------------------------------------------
81
82/// A message sent from L1 to L2 via the Inbox contract.
83#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
84pub struct L1ToL2Message {
85    /// The sender on L1.
86    pub sender: L1Actor,
87    /// The recipient on L2.
88    pub recipient: L2Actor,
89    /// Message content (application-specific payload).
90    pub content: Fr,
91    /// Hash of the secret needed to consume this message.
92    pub secret_hash: Fr,
93    /// Global index in the L1-to-L2 message tree.
94    pub index: Fr,
95}
96
97impl L1ToL2Message {
98    pub fn new(
99        sender: L1Actor,
100        recipient: L2Actor,
101        content: Fr,
102        secret_hash: Fr,
103        index: Fr,
104    ) -> Self {
105        Self {
106            sender,
107            recipient,
108            content,
109            secret_hash,
110            index,
111        }
112    }
113
114    pub fn empty() -> Self {
115        Self {
116            sender: L1Actor::empty(),
117            recipient: L2Actor::empty(),
118            content: Fr::zero(),
119            secret_hash: Fr::zero(),
120            index: Fr::zero(),
121        }
122    }
123
124    /// Serialize to field elements (6 total).
125    pub fn to_fields(&self) -> Vec<Fr> {
126        let s = self.sender.to_fields();
127        let r = self.recipient.to_fields();
128        vec![s[0], s[1], r[0], r[1], self.content, self.secret_hash]
129    }
130
131    /// Compute the message hash: `sha256_to_field(to_fields())`.
132    pub fn hash(&self) -> Fr {
133        let fields = self.to_fields();
134        let mut data = Vec::with_capacity(fields.len() * 32);
135        for f in &fields {
136            data.extend_from_slice(&f.to_be_bytes());
137        }
138        aztec_core::hash::sha256_to_field_pub(&data)
139    }
140}
141
142// ---------------------------------------------------------------------------
143// Claim types
144// ---------------------------------------------------------------------------
145
146/// Information needed to claim tokens bridged from L1 to L2.
147#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
148pub struct L2Claim {
149    /// Random secret for claiming.
150    pub claim_secret: Fr,
151    /// `poseidon2([secret], SECRET_HASH)`.
152    pub claim_secret_hash: Fr,
153    /// Keccak256 hash of the L1 message (from Inbox event).
154    pub message_hash: Fr,
155    /// Index in the L1-to-L2 message tree.
156    pub message_leaf_index: u64,
157}
158
159/// Claim information including the bridged amount.
160#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
161pub struct L2AmountClaim {
162    /// Base claim data.
163    #[serde(flatten)]
164    pub claim: L2Claim,
165    /// Amount of tokens bridged.
166    pub claim_amount: u128,
167}
168
169/// Claim information including amount and recipient.
170#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
171pub struct L2AmountClaimWithRecipient {
172    /// Amount claim data.
173    #[serde(flatten)]
174    pub amount_claim: L2AmountClaim,
175    /// L2 recipient address.
176    pub recipient: AztecAddress,
177}
178
179// ---------------------------------------------------------------------------
180// Utility functions
181// ---------------------------------------------------------------------------
182
183/// Generate a random claim secret and its hash.
184///
185/// Returns `(secret, secret_hash)` where
186/// `secret_hash = poseidon2([secret], SECRET_HASH)`.
187///
188/// Mirrors TS `generateClaimSecret()`.
189pub fn generate_claim_secret() -> (Fr, Fr) {
190    let secret = Fr::random();
191    let hash = aztec_core::hash::compute_secret_hash(&secret);
192    (secret, hash)
193}