diff --git a/Cargo.lock b/Cargo.lock index 92251b2d..3d6f31a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,17 +120,11 @@ dependencies = [ "agglayer-primitives", "agglayer-tries", "arbitrary", - "bincode", - "educe", "hex", "insta", "rstest", "serde", "serde_json", - "sp1-core-executor", - "sp1-hypercube", - "sp1-primitives", - "sp1-sdk", "thiserror 2.0.18", "unified-bridge", ] diff --git a/Cargo.toml b/Cargo.toml index 14f339fe..6243216b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,7 @@ agglayer-tries = { path = "crates/agglayer-tries", version = "0.14.0" } unified-bridge = { path = "crates/unified-bridge", version = "0.14.0" } sp1-build = "=6.0.2" -sp1-core-executor = "=6.0.2" -sp1-core-machine = "=6.0.2" -sp1-hypercube = "=6.0.2" sp1-sdk = "=6.0.2" -sp1-primitives = "=6.0.2" -sp1-prover = "=6.0.2" sp1-zkvm = { version = "=6.0.2", default-features = false } alloy = { version = "1.7.3", features = ["full"] } diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data.rs b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data.rs index 5317b102..4340168e 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data.rs +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data.rs @@ -1,13 +1,4 @@ -use bincode::Options as _; - use super::Error; mod types_to_v1; mod v1_to_types; - -#[inline] -fn sp1v4_bincode_options() -> impl bincode::Options { - bincode::DefaultOptions::new() - .with_big_endian() - .with_fixint_encoding() -} diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/types_to_v1.rs b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/types_to_v1.rs index 387a0709..4e3de999 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/types_to_v1.rs +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/types_to_v1.rs @@ -3,12 +3,32 @@ use std::collections::HashMap; use agglayer_interop_types::aggchain_proof::{ AggchainData, AggchainProof, MultisigPayload, Proof, SP1StarkWithContext, }; -use bincode::Options as _; use prost::bytes::Bytes; -use super::{sp1v4_bincode_options, Error}; +use super::Error; use crate::v1::{self}; +fn serialize_public_values(public_values: &T) -> Result +where + T: serde::Serialize + std::panic::RefUnwindSafe, +{ + Ok(Bytes::from( + std::panic::catch_unwind(|| bincode::serialize(public_values)) + .map_err(|_| { + Error::serializing_aggchain_proof_public_values(Box::new( + bincode::ErrorKind::Custom(String::from("panic")), + )) + })? + .map_err(Error::serializing_context)?, + )) +} + +fn serialize_generic_public_values( + public_values: &Option>, +) -> Result { + serialize_public_values(public_values) +} + impl TryFrom for v1::AggchainProof { type Error = Error; @@ -20,15 +40,7 @@ impl TryFrom for v1::AggchainProof { context: match value.public_values { Some(public_values) => HashMap::from([( "public_values".to_owned(), - Bytes::from( - std::panic::catch_unwind(|| bincode::serialize(&*public_values)) - .map_err(|_| { - Error::serializing_aggchain_proof_public_values(Box::new( - bincode::ErrorKind::Custom(String::from("panic")), - )) - })? - .map_err(Error::serializing_context)?, - ), + serialize_public_values(&public_values)?, )]), None => HashMap::new(), }, @@ -46,22 +58,8 @@ impl TryFrom for v1::aggchain_proof::Proof { vkey, version, }) => Ok(v1::aggchain_proof::Proof::Sp1Stark(v1::Sp1StarkProof { - proof: std::panic::catch_unwind(|| sp1v4_bincode_options().serialize(&proof)) - .map_err(|_| { - Error::serializing_proof(Box::new(bincode::ErrorKind::Custom( - String::from("panic"), - ))) - })? - .map_err(Error::serializing_context)? - .into(), - vkey: std::panic::catch_unwind(|| sp1v4_bincode_options().serialize(&vkey)) - .map_err(|_| { - Error::serializing_vkey(Box::new(bincode::ErrorKind::Custom(String::from( - "panic", - )))) - })? - .map_err(Error::serializing_context)? - .into(), + proof: proof.into(), + vkey: vkey.into(), version, })), } @@ -87,15 +85,7 @@ impl TryFrom for v1::AggchainData { } => v1::aggchain_data::Data::Generic(v1::AggchainProof { context: HashMap::from([( "public_values".to_owned(), - Bytes::from( - std::panic::catch_unwind(|| bincode::serialize(&public_values)) - .map_err(|_| { - Error::serializing_aggchain_proof_public_values(Box::new( - bincode::ErrorKind::Custom(String::from("panic")), - )) - })? - .map_err(Error::serializing_context)?, - ), + serialize_generic_public_values(&public_values)?, )]), aggchain_params: Some(aggchain_params.into()), signature: signature.map(|signature| v1::FixedBytes65 { @@ -103,26 +93,8 @@ impl TryFrom for v1::AggchainData { }), proof: Some(v1::aggchain_proof::Proof::Sp1Stark(v1::Sp1StarkProof { version: proof.version, - proof: std::panic::catch_unwind(|| { - sp1v4_bincode_options().serialize(&proof.proof) - }) - .map_err(|_| { - Error::serializing_proof(Box::new(bincode::ErrorKind::Custom( - String::from("panic"), - ))) - })? - .map_err(Error::serializing_proof)? - .into(), - vkey: std::panic::catch_unwind(|| { - sp1v4_bincode_options().serialize(&proof.vkey) - }) - .map_err(|_| { - Error::serializing_vkey(Box::new(bincode::ErrorKind::Custom( - String::from("panic"), - ))) - })? - .map_err(Error::serializing_vkey)? - .into(), + proof: proof.proof.into(), + vkey: proof.vkey.into(), })), }), AggchainData::MultisigOnly { multisig } => { diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/v1_to_types.rs b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/v1_to_types.rs index f23b7801..fbd4d8b1 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/v1_to_types.rs +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/aggchain_data/v1_to_types.rs @@ -1,14 +1,32 @@ use agglayer_interop_types::aggchain_proof::{ AggchainData, AggchainProof, MultisigPayload, Proof, SP1StarkWithContext, }; -use bincode::Options as _; -use super::{sp1v4_bincode_options, Error}; +use super::Error; use crate::v1::{self}; /// Maximum number of signers allowed in a multisig payload. const MAX_SIGNERS: usize = 1024; +fn deserialize_public_values(bytes: &[u8]) -> Result +where + T: serde::de::DeserializeOwned, +{ + std::panic::catch_unwind(|| bincode::deserialize(bytes)) + .map_err(|_| { + Error::deserializing_aggchain_proof_public_values(Box::new(bincode::ErrorKind::Custom( + String::from("panic"), + ))) + })? + .map_err(Error::deserializing_aggchain_proof_public_values) +} + +fn deserialize_generic_public_values( + bytes: &[u8], +) -> Result>, Error> { + deserialize_public_values(bytes) +} + impl TryFrom for AggchainProof { type Error = Error; @@ -19,16 +37,7 @@ impl TryFrom for AggchainProof { public_values: value .context .get("public_values") - .map(|b| { - std::panic::catch_unwind(|| bincode::deserialize(b)) - .map_err(|_| { - Error::deserializing_aggchain_proof_public_values(Box::new( - bincode::ErrorKind::Custom(String::from("panic")), - )) - })? - .map_err(Error::deserializing_aggchain_proof_public_values) - .map(Box::new) - }) + .map(|b| deserialize_public_values(b).map(Box::new)) .transpose()?, }) } @@ -55,22 +64,8 @@ impl TryFrom for Proof { } = value; Ok(Proof::SP1Stark(SP1StarkWithContext { - proof: Box::new( - std::panic::catch_unwind(|| sp1v4_bincode_options().deserialize(&proof)) - .map_err(|_| { - Error::deserializing_proof(Box::new(bincode::ErrorKind::Custom( - String::from("panic"), - ))) - })? - .map_err(Error::deserializing_proof)?, - ), - vkey: std::panic::catch_unwind(|| sp1v4_bincode_options().deserialize(&vkey)) - .map_err(|_| { - Error::deserializing_vkey(Box::new(bincode::ErrorKind::Custom(String::from( - "panic", - )))) - })? - .map_err(Error::deserializing_vkey)?, + proof: proof.to_vec(), + vkey: vkey.to_vec(), version, })) } @@ -98,11 +93,15 @@ impl TryFrom for AggchainData { .transpose()? .map(Box::new); - let AggchainProof { - public_values, - aggchain_params, - proof, - } = aggchain_proof.try_into()?; + let public_values = aggchain_proof + .context + .get("public_values") + .map(|b| deserialize_generic_public_values(b)) + .transpose()? + .unwrap_or(None); + + let proof = required_field!(aggchain_proof, proof); + let aggchain_params = required_field!(aggchain_proof, aggchain_params); AggchainData::Generic { public_values, diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data__debug.snap index c07f3119..9f361d51 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data__debug.snap @@ -5,4 +5,4 @@ expression: "format!(\"{:?}\", eyre::Error::from(error))" invalid value Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_field__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_field__debug.snap index e0cdb294..22b83642 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_field__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_field__debug.snap @@ -5,4 +5,4 @@ expression: "format!(\"{:?}\", eyre::Error::from(error))" value: invalid value Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_nested__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_nested__debug.snap index daff0557..9ccc822f 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_nested__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_data_in_nested__debug.snap @@ -5,4 +5,4 @@ expression: "format!(\"{:?}\", eyre::Error::from(error))" data.value: invalid value Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig__debug.snap index 65f9640e..407662f7 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig__debug.snap @@ -8,4 +8,4 @@ Caused by: invalid parity: 5 Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig_in_nested__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig_in_nested__debug.snap index 3a46c270..a079c92f 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig_in_nested__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__bad_sig_in_nested__debug.snap @@ -8,4 +8,4 @@ Caused by: invalid parity: 5 Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_deser__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_deser__debug.snap index b286714d..a0fef6f2 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_deser__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_deser__debug.snap @@ -8,4 +8,4 @@ Caused by: failed Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_ser__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_ser__debug.snap index 9bec4c64..5d28411d 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_ser__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__failed_ser__debug.snap @@ -8,4 +8,4 @@ Caused by: failed Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__no_proof__debug.snap b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__no_proof__debug.snap index ad893ff3..bc8bbdb6 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__no_proof__debug.snap +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/snapshots/agglayer_interop_grpc_types__compat__v1__tests__no_proof__debug.snap @@ -5,4 +5,4 @@ expression: "format!(\"{:?}\", eyre::Error::from(error))" proof: required field is missing Location: - crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:25:25 + crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs:30:25 diff --git a/crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs b/crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs index 3855ab9d..6ddda6e8 100644 --- a/crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs +++ b/crates/agglayer-interop-grpc-types/src/compat/v1/tests.rs @@ -1,7 +1,12 @@ use agglayer_interop_types::{ - aggchain_proof::AggchainData, primitives::SignatureError, Address, BridgeExit, - ClaimFromMainnet, ClaimFromRollup, Digest, GlobalIndex, ImportedBridgeExit, L1InfoTreeLeaf, - L1InfoTreeLeafInner, MerkleProof, TokenInfo, U256, + aggchain_proof::{ + AggchainData, AggchainProof, AggchainProofPublicValues, MultisigPayload, Proof, + SP1StarkWithContext, + }, + primitives::SignatureError, + Address, BridgeExit, ClaimFromMainnet, ClaimFromRollup, Digest, GlobalIndex, + ImportedBridgeExit, L1InfoTreeLeaf, L1InfoTreeLeafInner, MerkleProof, NetworkId, Signature, + TokenInfo, U256, }; use prost::Message; @@ -89,6 +94,160 @@ macro_rules! make_round_trip_fuzzers { } make_round_trip_fuzzers!(fuzz_round_trip_address, v1::FixedBytes20, Address); + +fn sample_public_values() -> AggchainProofPublicValues { + AggchainProofPublicValues { + prev_local_exit_root: Digest([0x11; 32]), + new_local_exit_root: Digest([0x22; 32]), + l1_info_root: Digest([0x33; 32]), + origin_network: NetworkId::new(9), + commit_imported_bridge_exits: Digest([0x44; 32]), + aggchain_params: Digest([0x55; 32]), + } +} + +fn sample_proof() -> Proof { + Proof::SP1Stark(SP1StarkWithContext { + version: "v6.0.0".to_owned(), + proof: vec![0x06, 0x00, 0x00, 0x01], + vkey: vec![0xb6, 0x00, 0x02], + }) +} + +fn sample_signature() -> Signature { + Signature::new(U256::from(1_u64), U256::from(2_u64), true) +} + +fn sample_generic(public_values: Option>) -> AggchainData { + AggchainData::Generic { + proof: sample_proof(), + aggchain_params: Digest([0x77; 32]), + signature: None, + public_values, + } +} + +fn sample_aggchain_proof(public_values: Option>) -> AggchainProof { + AggchainProof { + proof: sample_proof(), + aggchain_params: Digest([0x88; 32]), + public_values, + } +} + +fn sample_multisig_and_aggchain_proof( + public_values: Option>, +) -> AggchainData { + AggchainData::MultisigAndAggchainProof { + multisig: MultisigPayload(vec![Some(sample_signature())]), + aggchain_proof: sample_aggchain_proof(public_values), + } +} + +#[rstest::rstest] +#[case::none(None)] +#[case::some(Some(Box::new(sample_public_values())))] +fn generic_v1_wire_preserves_legacy_public_values_shape( + #[case] public_values: Option>, +) { + let input = sample_generic(public_values.clone()); + + let proto: v1::AggchainData = input.clone().try_into().unwrap(); + let v1::aggchain_data::Data::Generic(proto_generic) = proto.data.as_ref().unwrap() else { + panic!("expected Generic aggchain data"); + }; + + let encoded = proto_generic + .context + .get("public_values") + .expect("Generic v1 wire format always includes public_values"); + let decoded: Option> = bincode::deserialize(encoded).unwrap(); + + assert_eq!(decoded, public_values); + assert_eq!(AggchainData::try_from(proto).unwrap(), input); +} + +#[rstest::rstest] +#[case::none(None)] +#[case::some(Some(Box::new(sample_public_values())))] +fn aggchain_proof_v1_wire_preserves_bare_public_values_shape( + #[case] public_values: Option>, +) { + let input = sample_aggchain_proof(public_values.clone()); + + let proto: v1::AggchainProof = input.clone().try_into().unwrap(); + + match public_values.clone() { + Some(public_values) => { + let encoded = proto + .context + .get("public_values") + .expect("AggchainProof v1 wire format includes public_values only when Some"); + let decoded: AggchainProofPublicValues = bincode::deserialize(encoded).unwrap(); + + assert_eq!(decoded, *public_values); + } + None => assert!(!proto.context.contains_key("public_values")), + } + + assert_eq!(AggchainProof::try_from(proto).unwrap(), input); +} + +#[rstest::rstest] +#[case::none(None)] +#[case::some(Some(Box::new(sample_public_values())))] +fn multisig_and_aggchain_proof_v1_wire_preserves_bare_public_values_shape( + #[case] public_values: Option>, +) { + let input = sample_multisig_and_aggchain_proof(public_values.clone()); + + let proto: v1::AggchainData = input.clone().try_into().unwrap(); + let v1::aggchain_data::Data::MultisigAndAggchainProof(proto_with_multisig) = + proto.data.as_ref().unwrap() + else { + panic!("expected MultisigAndAggchainProof aggchain data"); + }; + let proto_aggchain_proof = proto_with_multisig.aggchain_proof.as_ref().unwrap(); + + match public_values.clone() { + Some(public_values) => { + let encoded = proto_aggchain_proof + .context + .get("public_values") + .expect("MultisigAndAggchainProof uses bare AggchainProof encoding when Some"); + let decoded: AggchainProofPublicValues = bincode::deserialize(encoded).unwrap(); + + assert_eq!(decoded, *public_values); + } + None => assert!(!proto_aggchain_proof.context.contains_key("public_values")), + } + + assert_eq!(AggchainData::try_from(proto).unwrap(), input); +} + +#[rstest::rstest] +#[case("v4.0.0-rc.3", vec![0x04, 0x00, 0x03], vec![0xa4, 0x03])] +#[case("v6.0.0", vec![0x06, 0x00, 0x00, 0x01], vec![0xb6, 0x00, 0x02])] +fn sp1_stark_proof_round_trip_preserves_opaque_payload( + #[case] version: &str, + #[case] proof_bytes: Vec, + #[case] vkey_bytes: Vec, +) { + let proto = v1::Sp1StarkProof { + version: version.to_owned(), + proof: proof_bytes.clone().into(), + vkey: vkey_bytes.clone().into(), + }; + + let proof = Proof::try_from(proto.clone()).unwrap(); + let v1::aggchain_proof::Proof::Sp1Stark(round_trip) = + v1::aggchain_proof::Proof::try_from(proof).unwrap(); + + assert_eq!(round_trip.version, proto.version); + assert_eq!(round_trip.proof, proto.proof); + assert_eq!(round_trip.vkey, proto.vkey); +} + #[test] fn fuzz_round_trip_aggchain_data() { bolero::check!() @@ -105,32 +264,21 @@ fn fuzz_round_trip_aggchain_data() { match AggchainData::try_from(proto) { Ok(output) => { - // If we had empty multisig, this should not have succeeded assert!( !has_empty_multisig, "Expected error for empty multisig signatures, but conversion succeeded" ); - // For most cases, we can't check equality due to stark proofs, - // but we can at least verify the conversion succeeded - if let ( - AggchainData::ECDSA { signature: sig1 }, - AggchainData::ECDSA { signature: sig2 }, - ) = (&input, &output) - { - assert_eq!(sig1, sig2); - } + assert_eq!(input, &output); } Err(err) => { if has_empty_multisig { - // This error is expected for empty multisig cases let err_msg = err.to_string(); assert!( err_msg.contains("Multisig ECDSA doesn't have any signature"), "Expected empty multisig error, got: {err}", ); } else { - // Any other error should cause the test to fail panic!("Unexpected conversion error: {err}"); } } diff --git a/crates/agglayer-interop-types/Cargo.toml b/crates/agglayer-interop-types/Cargo.toml index 8e7501fe..c664c845 100644 --- a/crates/agglayer-interop-types/Cargo.toml +++ b/crates/agglayer-interop-types/Cargo.toml @@ -16,14 +16,7 @@ agglayer-primitives.workspace = true agglayer-tries.workspace = true unified-bridge.workspace = true -sp1-sdk.workspace = true -sp1-core-executor.workspace = true -sp1-hypercube.workspace = true -sp1-primitives.workspace = true - arbitrary = { workspace = true, optional = true } -bincode.workspace = true -educe.workspace = true hex.workspace = true serde.workspace = true thiserror.workspace = true diff --git a/crates/agglayer-interop-types/src/aggchain_proof/mod.rs b/crates/agglayer-interop-types/src/aggchain_proof/mod.rs index 6a0db907..9438af2e 100644 --- a/crates/agglayer-interop-types/src/aggchain_proof/mod.rs +++ b/crates/agglayer-interop-types/src/aggchain_proof/mod.rs @@ -1,10 +1,5 @@ use agglayer_primitives::Signature; -use educe::Educe; use serde::{Deserialize, Serialize}; -use sp1_core_executor::SP1RecursionProof; -use sp1_hypercube::SP1PcsProofInner; -use sp1_primitives::SP1GlobalContext; -use sp1_sdk::SP1VerifyingKey; pub use unified_bridge::AggchainProofPublicValues; use crate::Digest; @@ -52,69 +47,20 @@ pub struct AggchainProof { pub public_values: Option>, } -pub type SP1StarkProof = SP1RecursionProof; - -#[derive(Educe, Serialize, Deserialize, Clone)] -#[educe(Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] +#[cfg_attr(feature = "testutils", derive(arbitrary::Arbitrary, Eq, PartialEq))] pub struct SP1StarkWithContext { - pub proof: Box, - #[educe(Debug(ignore))] - pub vkey: SP1VerifyingKey, + pub proof: Vec, + pub vkey: Vec, pub version: String, } /// Proof that is part of the aggchain proof submitted via the [`Certificate`]. #[derive(Serialize, Deserialize, Clone, Debug)] +#[cfg_attr(feature = "testutils", derive(arbitrary::Arbitrary, Eq, PartialEq))] pub enum Proof { SP1Stark(SP1StarkWithContext), } -#[cfg(feature = "testutils")] -impl<'a> arbitrary::Arbitrary<'a> for Proof { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - use bincode::Options as _; - let bytes = <&[u8]>::arbitrary(u)?; - let proof = std::panic::catch_unwind(|| { - bincode::options() - .with_limit(bytes.len() as u64) - .deserialize(bytes) - }) - .map_err(|_| arbitrary::Error::IncorrectFormat)? - .map_err(|e| match *e { - bincode::ErrorKind::SizeLimit => arbitrary::Error::NotEnoughData, - _ => arbitrary::Error::IncorrectFormat, - })?; - - let bytes = <&[u8]>::arbitrary(u)?; - let vkey = std::panic::catch_unwind(|| { - bincode::options() - .with_limit(bytes.len() as u64) - .deserialize(bytes) - }) - .map_err(|_| arbitrary::Error::IncorrectFormat)? - .map_err(|e| match *e { - bincode::ErrorKind::SizeLimit => arbitrary::Error::NotEnoughData, - _ => arbitrary::Error::IncorrectFormat, - })?; - Ok(Proof::SP1Stark( - crate::aggchain_proof::SP1StarkWithContext { - proof, - vkey, - version: String::arbitrary(u)?, - }, - )) - } -} - -#[cfg(feature = "testutils")] -impl std::cmp::PartialEq for Proof { - fn eq(&self, other: &Self) -> bool { - bincode::serialize(self).unwrap() == bincode::serialize(other).unwrap() - } -} - -#[cfg(feature = "testutils")] -impl std::cmp::Eq for Proof {} - #[cfg(test)] mod test; diff --git a/crates/agglayer-interop-types/src/aggchain_proof/test.rs b/crates/agglayer-interop-types/src/aggchain_proof/test.rs index da41fc1c..b0fa1833 100644 --- a/crates/agglayer-interop-types/src/aggchain_proof/test.rs +++ b/crates/agglayer-interop-types/src/aggchain_proof/test.rs @@ -32,3 +32,43 @@ fn aggchaindata_json(#[case] name: &str, #[case] aggchain_data: AggchainData) { let json_str = serde_json::to_string_pretty(&container).expect("serialization failed"); insta::assert_snapshot!(name, json_str); } + +#[test] +fn proof_bincode_round_trip_preserves_raw_envelope() { + let proof = Proof::SP1Stark(SP1StarkWithContext { + version: "v6.0.0".to_owned(), + proof: vec![0xde, 0xad, 0xbe, 0xef], + vkey: vec![0xca, 0xfe, 0xba, 0xbe], + }); + + let encoded = crate::bincode::default().serialize(&proof).unwrap(); + let decoded: Proof = crate::bincode::default().deserialize(&encoded).unwrap(); + + match decoded { + Proof::SP1Stark(decoded) => { + assert_eq!(decoded.version, "v6.0.0"); + assert_eq!(decoded.proof, vec![0xde, 0xad, 0xbe, 0xef]); + assert_eq!(decoded.vkey, vec![0xca, 0xfe, 0xba, 0xbe]); + } + } +} + +#[test] +fn proof_serde_round_trip_preserves_raw_envelope() { + let proof = Proof::SP1Stark(SP1StarkWithContext { + version: "v4.0.0-rc.3".to_owned(), + proof: vec![0xde, 0xad, 0xbe, 0xef], + vkey: vec![0xca, 0xfe, 0xba, 0xbe], + }); + + let encoded = serde_json::to_string(&proof).unwrap(); + let decoded: Proof = serde_json::from_str(&encoded).unwrap(); + + match decoded { + Proof::SP1Stark(decoded) => { + assert_eq!(decoded.version, "v4.0.0-rc.3"); + assert_eq!(decoded.proof, vec![0xde, 0xad, 0xbe, 0xef]); + assert_eq!(decoded.vkey, vec![0xca, 0xfe, 0xba, 0xbe]); + } + } +}