pessimistic_proof_core/aggchain_data/
mod.rs

1//! Aggchain proof data structures.
2//!
3//! The aggchain-proof is the extra custom logic which is verified within the
4//! pessimistic-proof.
5//!
6//! For now, this is constraint to be either one ECDSA signature, or one SP1
7//! stark proof proving a specified statement which can be abstracted here.
8
9use agglayer_primitives::{Address, Digest, Signature};
10use serde::{Deserialize, Serialize};
11
12pub use crate::aggchain_data::{
13    aggchain_hash::AggchainHashValues,
14    aggchain_proof::AggchainProof,
15    multisig::{MultiSignature, MultisigError},
16};
17use crate::{
18    local_state::commitment::{
19        PessimisticRootCommitmentVersion, SignatureCommitmentValues, SignatureCommitmentVersion,
20    },
21    proof::ConstrainedValues,
22    ProofError,
23};
24
25mod aggchain_hash;
26mod aggchain_proof;
27mod multisig;
28
29pub type Vkey = [u32; 8];
30
31/// Chain proof which include either multisig, aggchain proof, or both.
32/// Explicit enum which forbid the case where we have none of them.
33#[derive(Clone, Debug, Serialize, Deserialize)]
34pub enum AggchainData {
35    /// Legacy signature with migration logic
36    LegacyEcdsa {
37        /// Signer committing to the state transition.
38        signer: Address,
39        /// Signature committing to the state transition.
40        signature: Signature,
41    },
42    /// Multisig only
43    MultisigOnly(MultiSignature),
44    /// Multisig and an aggchain proof
45    MultisigAndAggchainProof {
46        /// Multisig
47        multisig: MultiSignature,
48        /// Aggchain proof
49        aggchain_proof: AggchainProof,
50    },
51}
52
53impl AggchainData {
54    /// Returns the aggchain hash
55    pub fn aggchain_hash(&self) -> Digest {
56        AggchainHashValues::from(self).hash()
57    }
58
59    pub fn verify(
60        &self,
61        constrained_values: ConstrainedValues,
62    ) -> Result<PessimisticRootCommitmentVersion, ProofError> {
63        let prev_pp_root_version = constrained_values.prev_pessimistic_root_version;
64
65        let target_pp_root_version = match self {
66            AggchainData::LegacyEcdsa { signer, signature } => {
67                AggchainData::legacy_ecdsa(signer, signature, constrained_values)?
68            }
69            AggchainData::MultisigOnly(multisig) => {
70                let commitment =
71                    SignatureCommitmentValues::new(&constrained_values, None).multisig_commitment();
72
73                multisig
74                    .verify(commitment)
75                    .map_err(ProofError::InvalidMultisig)?;
76
77                // Multisig is currently always on commitment v3
78                PessimisticRootCommitmentVersion::V3
79            }
80            AggchainData::MultisigAndAggchainProof {
81                multisig,
82                aggchain_proof,
83            } => {
84                let commitment = SignatureCommitmentValues::new(
85                    &constrained_values,
86                    Some(aggchain_proof.aggchain_params), // signs the same used by the stark
87                )
88                .multisig_commitment();
89
90                multisig
91                    .verify(commitment)
92                    .map_err(ProofError::InvalidMultisig)?;
93
94                // Panic upon invalid proof.
95                aggchain_proof.verify_aggchain_proof(&constrained_values);
96
97                // Multisig is currently always on commitment v3
98                PessimisticRootCommitmentVersion::V3
99            }
100        };
101
102        match (prev_pp_root_version, target_pp_root_version) {
103            // From V2 to V2: OK
104            (PessimisticRootCommitmentVersion::V2, PessimisticRootCommitmentVersion::V2) => {}
105            // From V3 to V3: OK
106            (PessimisticRootCommitmentVersion::V3, PessimisticRootCommitmentVersion::V3) => {}
107            // From V2 to V3: OK (migration)
108            (PessimisticRootCommitmentVersion::V2, PessimisticRootCommitmentVersion::V3) => {}
109            // Inconsistent signed payload.
110            _ => return Err(ProofError::InconsistentSignedPayload),
111        }
112
113        Ok(target_pp_root_version)
114    }
115}
116
117impl AggchainData {
118    /// Returns the target PP root version based on the signature.
119    pub fn legacy_ecdsa(
120        signer: &Address,
121        signature: &Signature,
122        constrained_values: ConstrainedValues,
123    ) -> Result<PessimisticRootCommitmentVersion, ProofError> {
124        let signature_values = SignatureCommitmentValues::new(&constrained_values, None);
125
126        let is_signed_with_version = |version: SignatureCommitmentVersion| {
127            let prehash = signature_values.commitment(version);
128            signature
129                .recover_address_from_prehash(&prehash)
130                .map(|recovered| *signer == recovered)
131                .map_err(|_| ProofError::InvalidSignature)
132        };
133
134        let target_pp_root_version = if is_signed_with_version(SignatureCommitmentVersion::V3)?
135            || is_signed_with_version(SignatureCommitmentVersion::V5)?
136        {
137            PessimisticRootCommitmentVersion::V3
138        } else if is_signed_with_version(SignatureCommitmentVersion::V2)? {
139            PessimisticRootCommitmentVersion::V2
140        } else {
141            return Err(ProofError::InvalidSignature);
142        };
143
144        Ok(target_pp_root_version)
145    }
146}