1use agglayer_bincode as bincode;
2use agglayer_primitives::{Address, Digest};
3use agglayer_tries::roots::LocalExitRoot;
4use hex_literal::hex;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7use unified_bridge::{
8 Error, GlobalIndex, ImportedBridgeExitCommitmentValues, ImportedBridgeExitCommitmentVersion,
9 LocalExitTreeError, NetworkId, TokenInfo,
10};
11
12use crate::{
13 aggchain_data::MultisigError,
14 local_state::{
15 commitment::{
16 PessimisticRootCommitmentValues, PessimisticRootCommitmentVersion, StateCommitment,
17 },
18 NetworkState,
19 },
20 multi_batch_header::MultiBatchHeader,
21};
22
23pub const IMPORTED_BRIDGE_EXIT_COMMITMENT_VERSION: ImportedBridgeExitCommitmentVersion =
28 ImportedBridgeExitCommitmentVersion::V3;
29
30#[derive(Clone, Error, Debug, Serialize, Deserialize, PartialEq, Eq)]
38pub enum ProofError {
39 #[error("Invalid previous local exit root. declared: {declared}, computed: {computed}")]
42 InvalidPreviousLocalExitRoot { declared: Digest, computed: Digest },
43 #[error("Invalid previous balance root. declared: {declared}, computed: {computed}")]
44 InvalidPreviousBalanceRoot { declared: Digest, computed: Digest },
45 #[error("Invalid previous nullifier root. declared: {declared}, computed: {computed}")]
46 InvalidPreviousNullifierRoot { declared: Digest, computed: Digest },
47 #[error("Invalid new local exit root. declared: {declared}, computed: {computed}")]
48 InvalidNewLocalExitRoot { declared: Digest, computed: Digest },
49 #[error("Invalid new balance root. declared: {declared}, computed: {computed}")]
50 InvalidNewBalanceRoot { declared: Digest, computed: Digest },
51 #[error("Invalid new nullifier root. declared: {declared}, computed: {computed}")]
52 InvalidNewNullifierRoot { declared: Digest, computed: Digest },
53
54 #[error("Invalid imported bridge exit. global index: {global_index:?}, error: {source}")]
56 InvalidImportedBridgeExit {
57 source: Error,
58 global_index: GlobalIndex,
59 },
60
61 #[error(
63 "Invalid commitment on the imported bridge exits. declared: {declared}, computed: \
64 {computed}"
65 )]
66 InvalidImportedExitsRoot { declared: Digest, computed: Digest },
67
68 #[error("Mismatch between the imported bridge exits list and its commitment.")]
70 MismatchImportedExitsRoot,
71
72 #[error("Invalid nullifier path.")]
74 InvalidNullifierPath,
75
76 #[error("Invalid balance path.")]
78 InvalidBalancePath,
79
80 #[error("Balance overflow in bridge exit.")]
82 BalanceOverflowInBridgeExit,
83
84 #[error("Balance underflow in bridge exit.")]
86 BalanceUnderflowInBridgeExit,
87
88 #[error("Cannot perform bridge exit to the same network as the origin.")]
91 CannotExitToSameNetwork,
92
93 #[error("Invalid message origin network.")]
95 InvalidMessageOriginNetwork,
96
97 #[error("Invalid L1 TokenInfo. TokenInfo: {0:?}")]
99 InvalidL1TokenInfo(TokenInfo),
100
101 #[error("Missing token balance proof. TokenInfo: {0:?}")]
103 MissingTokenBalanceProof(TokenInfo),
104
105 #[error("Duplicate token in balance proofs. TokenInfo: {0:?}")]
107 DuplicateTokenBalanceProof(TokenInfo),
108
109 #[error("Invalid signature.")]
111 InvalidSignature,
112
113 #[error("Invalid signer. declared: {declared}, recovered: {recovered}")]
116 InvalidSigner {
117 declared: Address,
118 recovered: Address,
119 },
120
121 #[error(transparent)]
123 InvalidLocalExitTreeOperation(#[from] LocalExitTreeError),
124
125 #[error("Unknown error: {0}")]
127 Unknown(String),
128
129 #[error(
131 "Invalid previous pessimistic root. declared: {declared}, ppr_v2: {computed_v2}, ppr_v3: \
132 {computed_v3}"
133 )]
134 InvalidPreviousPessimisticRoot {
135 declared: Digest,
136 computed_v2: Digest,
137 computed_v3: Digest,
138 },
139
140 #[error("Inconsistent signed payload version.")]
142 InconsistentSignedPayload,
143
144 #[error("Height overflow")]
146 HeightOverflow,
147
148 #[error("Invalid multisig")]
150 InvalidMultisig(#[source] MultisigError),
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
155pub struct PessimisticProofOutput {
156 pub prev_local_exit_root: LocalExitRoot,
158 pub prev_pessimistic_root: Digest,
160 pub l1_info_root: Digest,
163 pub origin_network: NetworkId,
165 pub aggchain_hash: Digest,
167 pub new_local_exit_root: LocalExitRoot,
169 pub new_pessimistic_root: Digest,
171}
172
173impl PessimisticProofOutput {
174 pub fn bincode_codec() -> bincode::Codec<impl bincode::Options> {
175 bincode::contracts()
176 }
177}
178
179pub const EMPTY_LER: LocalExitRoot = LocalExitRoot::new(Digest(hex!(
180 "27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757"
181)));
182
183pub const EMPTY_PP_ROOT_V2: Digest = Digest(hex!(
184 "c89c9c0f2ebd19afa9e5910097c43e56fb4aff3a06ddee8d7c9bae09bc769184"
185));
186
187#[derive(Clone)]
190pub struct ConstrainedValues {
191 pub initial_state_commitment: StateCommitment,
192 pub final_state_commitment: StateCommitment,
193 pub prev_pessimistic_root: Digest,
194 pub prev_pessimistic_root_version: PessimisticRootCommitmentVersion,
195 pub height: u64,
196 pub origin_network: NetworkId,
197 pub l1_info_root: Digest,
198 pub commit_imported_bridge_exits: ImportedBridgeExitCommitmentValues,
199 pub certificate_id: Digest,
200}
201
202impl ConstrainedValues {
203 fn try_new(
204 batch_header: &MultiBatchHeader,
205 initial_state_commitment: &StateCommitment,
206 final_state_commitment: &StateCommitment,
207 ) -> Result<Self, ProofError> {
208 let settled_prev_pp_root = batch_header.prev_pessimistic_root;
209
210 let prev_pessimistic_root_version = PessimisticRootCommitmentValues {
213 balance_root: initial_state_commitment.balance_root,
214 nullifier_root: initial_state_commitment.nullifier_root,
215 ler_leaf_count: initial_state_commitment.ler_leaf_count,
216 height: batch_header.height,
217 origin_network: batch_header.origin_network,
218 }
219 .infer_settled_pp_root_version(settled_prev_pp_root)?;
220
221 Ok(Self {
222 initial_state_commitment: initial_state_commitment.clone(),
223 final_state_commitment: final_state_commitment.clone(),
224 height: batch_header.height,
225 origin_network: batch_header.origin_network,
226 l1_info_root: batch_header.l1_info_root,
227 commit_imported_bridge_exits: batch_header.commit_imported_bridge_exits(),
228 certificate_id: batch_header.certificate_id,
229 prev_pessimistic_root: settled_prev_pp_root,
230 prev_pessimistic_root_version,
231 })
232 }
233}
234
235pub fn generate_pessimistic_proof(
238 initial_network_state: NetworkState,
239 batch_header: &MultiBatchHeader,
240) -> Result<(PessimisticProofOutput, StateCommitment), ProofError> {
241 let initial_state_commitment = initial_network_state.get_state_commitment();
243 let mut network_state: NetworkState = initial_network_state;
244 let final_state_commitment = network_state.apply_batch_header(batch_header)?;
245
246 let constrained_values = ConstrainedValues::try_new(
248 batch_header,
249 &initial_state_commitment,
250 &final_state_commitment,
251 )?;
252
253 let target_pp_root_version = batch_header.aggchain_data.verify(constrained_values)?;
255
256 let height = batch_header
257 .height
258 .checked_add(1)
259 .ok_or(ProofError::HeightOverflow)?;
260
261 let new_pessimistic_root = PessimisticRootCommitmentValues {
262 balance_root: final_state_commitment.balance_root,
263 nullifier_root: final_state_commitment.nullifier_root,
264 ler_leaf_count: final_state_commitment.ler_leaf_count,
265 height,
266 origin_network: batch_header.origin_network,
267 }
268 .compute_pp_root(target_pp_root_version);
269
270 Ok((
271 PessimisticProofOutput {
272 prev_local_exit_root: zero_if_empty_local_exit_root(initial_state_commitment.exit_root),
273 prev_pessimistic_root: batch_header.prev_pessimistic_root,
274 l1_info_root: batch_header.l1_info_root,
275 origin_network: batch_header.origin_network,
276 aggchain_hash: batch_header.aggchain_data.aggchain_hash(),
277 new_local_exit_root: zero_if_empty_local_exit_root(final_state_commitment.exit_root),
278 new_pessimistic_root,
279 },
280 final_state_commitment,
281 ))
282}
283
284pub fn zero_if_empty_local_exit_root(root: LocalExitRoot) -> LocalExitRoot {
288 if root == EMPTY_LER {
289 LocalExitRoot::default()
290 } else {
291 root
292 }
293}