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}