Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
711 changes: 356 additions & 355 deletions Cargo.lock

Large diffs are not rendered by default.

39 changes: 19 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,32 @@ pessimistic-proof-core = { path = "crates/pessimistic-proof-core" }
pessimistic-proof-test-suite = { path = "crates/pessimistic-proof-test-suite" }

# Interop
agglayer-bincode = "0.17.0"
agglayer-interop = "0.17.0"
agglayer-interop-types = "0.17.0"
agglayer-elf-build = "0.17.0"
agglayer-primitives = "0.17.0"
agglayer-tries = "0.17.0"
unified-bridge = "0.17.0"
agglayer-bincode = "0.18.0"
agglayer-interop = "0.18.0"
agglayer-interop-types = "0.18.0"
agglayer-elf-build = "0.18.0"
agglayer-primitives = "0.18.0"
agglayer-tries = "0.18.0"
unified-bridge = "0.18.0"

# Provers
prover-alloy = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prover-config = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prover-logger = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prover-executor = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prover-engine = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prprover-uti = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.5" }
prover-alloy = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.6" }
prover-config = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.6" }
prover-logger = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.6" }
prover-executor = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.6" }
prover-engine = { git = "https://github.com/agglayer/provers.git", tag = "v2.0.0-rc.6" }

# SP1 dependencies
# Note: Whenever these are updated, also consider updating the SP1 toolchain docker image version.
# It is specified at the top of scripts/make/Makefile.pp.toml.
sp1-core-machine = "=6.2.1"
sp1-core-executor = "=6.2.1"
sp1-hypercube = "=6.2.1"
sp1-sdk = { version = "=6.2.1", features = ["blocking"] }
sp1-core-machine = "=6.2.2"
sp1-core-executor = "=6.2.2"
sp1-hypercube = "=6.2.2"
sp1-sdk = { version = "=6.2.2", features = ["blocking"] }
sp1-sdk-v5 = { package = "sp1-sdk", version = "=5.2.2" }
sp1-primitives = "=6.2.1"
sp1-prover = "=6.2.1"
sp1-zkvm = { version = "=6.2.1", default-features = false }
sp1-primitives = "=6.2.2"
sp1-prover = "=6.2.2"
sp1-zkvm = { version = "=6.2.2", default-features = false }

# Core dependencies
alloy = { version = "1.7.3", features = ["full", "json-rpc"] }
Expand Down
3 changes: 2 additions & 1 deletion crates/agglayer-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ thiserror.workspace = true
tokio-util.workspace = true
tokio.workspace = true
tracing.workspace = true
ulid = { workspace = true, features = ["serde"] }

agglayer-config.workspace = true
agglayer-sp1.workspace = true
Expand All @@ -35,6 +34,8 @@ pessimistic-proof.workspace = true
mockall = { workspace = true, optional = true }

[dev-dependencies]
arbitrary.workspace = true
bolero.workspace = true
criterion.workspace = true
alloy-primitives.workspace = true
fail = { workspace = true, features = ["failpoints"] }
Expand Down
6 changes: 3 additions & 3 deletions crates/agglayer-storage/src/columns/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ pub const SETTLEMENT_JOB_RESULTS_CF: &str = "settlement_job_results_cf";
pub const SETTLEMENT_ATTEMPTS_COLUMN_OPTIONS: ColumnOptions = ColumnOptions {
compression: crate::schema::options::ColumnCompressionType::Lz4,
prefix_extractor: crate::schema::options::PrefixExtractor::Fixed {
size: 16, // settlement_job_id (Ulid)
size: crate::types::settlement::attempt::Key::PREFIX_LEN,
},
};

pub const SETTLEMENT_ATTEMPT_RESULTS_COLUMN_OPTIONS: ColumnOptions = ColumnOptions {
compression: crate::schema::options::ColumnCompressionType::Lz4,
prefix_extractor: crate::schema::options::PrefixExtractor::Fixed {
size: 16, // settlement_job_id (Ulid)
size: crate::types::settlement::attempt::Key::PREFIX_LEN,
},
};

pub const SETTLEMENT_ATTEMPT_PER_WALLET_COLUMN_OPTIONS: ColumnOptions = ColumnOptions {
compression: crate::schema::options::ColumnCompressionType::Lz4,
prefix_extractor: crate::schema::options::PrefixExtractor::Fixed {
size: 28, // address (20 bytes) + nonce (u64)
size: crate::types::settlement::attempt_per_wallet::Key::PREFIX_LEN,
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ use crate::{
#[test]
fn settlement_attempt_result_roundtrip_codec() {
let key = attempt::Key {
settlement_job_id: SettlementJobId::from(ulid::Ulid::from(7u128)),
settlement_job_id: SettlementJobId::from(7u128),
attempt_sequence_number: 3,
};
let value = SettlementAttemptResult::contract_call_success_for_test(23);

let encoded_key = key.encode().expect("Unable to encode key");
let expected_key = [
key.settlement_job_id.to_be_bytes().as_slice(),
&3_u64.to_be_bytes(),
]
.concat();
assert_eq!(encoded_key.len(), Key::LEN);
assert_eq!(encoded_key, expected_key);

let decoded_key = Key::decode(&encoded_key).expect("Unable to decode key");
assert_eq!(decoded_key.settlement_job_id, key.settlement_job_id);
assert_eq!(
Expand Down
12 changes: 10 additions & 2 deletions crates/agglayer-storage/src/columns/settlement_attempts/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ use crate::{
#[test]
fn settlement_attempt_roundtrip_codec() {
let key = Key {
settlement_job_id: SettlementJobId::from(ulid::Ulid::from(24u128)),
settlement_job_id: SettlementJobId::from(24u128),
attempt_sequence_number: 2,
};
let value = mk_settlement_attempt(2);

let encoded_key = key.encode().expect("Unable to encode key");
let expected_key = [
key.settlement_job_id.to_be_bytes().as_slice(),
&2_u64.to_be_bytes(),
]
.concat();
assert_eq!(encoded_key.len(), Key::LEN);
assert_eq!(encoded_key, expected_key);

let decoded_key = Key::decode(&encoded_key).expect("Unable to decode key");
assert_eq!(decoded_key.settlement_job_id, key.settlement_job_id);
assert_eq!(
Expand All @@ -38,7 +46,7 @@ fn settlement_attempt_key_ordering_is_stable_for_same_job() {
let tmp = TempDBDir::new();
let db = StateStore::init_db(tmp.path.as_path()).expect("Unable to init db");

let settlement_job_id = SettlementJobId::from(ulid::Ulid::from(99u128));
let settlement_job_id = SettlementJobId::from(99u128);
for seq in [1u64, 2, 3, 4, 5] {
let key = Key {
settlement_job_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ use crate::{

#[test]
fn settlement_job_result_roundtrip_codec() {
let key = SettlementJobId::from(ulid::Ulid::from(7u128));
let key = SettlementJobId::from(7u128);
let value = SettlementJobResult::contract_call_success_for_test(23);

let encoded_key = key.encode().expect("Unable to encode key");
assert_eq!(encoded_key.len(), Key::BYTE_LEN);
assert_eq!(encoded_key, key.to_be_bytes());

let decoded_key = Key::decode(&encoded_key).expect("Unable to decode key");
assert_eq!(decoded_key, key);

Expand Down
5 changes: 4 additions & 1 deletion crates/agglayer-storage/src/columns/settlement_jobs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ use crate::{

#[test]
fn settlement_job_roundtrip_codec() {
let key = SettlementJobId::from(ulid::Ulid::from(42u128));
let key = SettlementJobId::from(42u128);
let value = mk_settlement_job();

let encoded_key = key.encode().expect("Unable to encode key");
assert_eq!(encoded_key.len(), Key::BYTE_LEN);
assert_eq!(encoded_key, key.to_be_bytes());

let decoded_key = Key::decode(&encoded_key).expect("Unable to decode key");
assert_eq!(decoded_key, key);

Expand Down
32 changes: 32 additions & 0 deletions crates/agglayer-storage/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,35 @@ pub mod options;
pub use codec::{bincode_codec, Codec, CodecError};
pub(crate) use codec::{impl_codec_using_bincode_for, impl_codec_using_protobuf_for};
pub use column::{ColumnDescriptor, ColumnSchema};

pub(crate) const U32_LEN: usize = std::mem::size_of::<u32>();
pub(crate) const U64_LEN: usize = std::mem::size_of::<u64>();
pub(crate) const U128_LEN: usize = std::mem::size_of::<u128>();

/// Converts a byte slice into an array with an exact, schema-defined length.
pub(crate) fn fixed_bytes<const N: usize>(
bytes: &[u8],
field: &'static str,
) -> Result<[u8; N], CodecError> {
bytes.try_into().map_err(|_| {
CodecError::Conversion(format!(
"{field} must be {N} bytes long, got {}",
bytes.len()
))
})
}

/// Decodes a big-endian `u32` from a fixed-width storage field.
pub(crate) fn decode_u32_be(bytes: &[u8], field: &'static str) -> Result<u32, CodecError> {
Ok(u32::from_be_bytes(fixed_bytes::<U32_LEN>(bytes, field)?))
}

/// Decodes a big-endian `u64` from a fixed-width storage field.
pub(crate) fn decode_u64_be(bytes: &[u8], field: &'static str) -> Result<u64, CodecError> {
Ok(u64::from_be_bytes(fixed_bytes::<U64_LEN>(bytes, field)?))
}

/// Decodes a big-endian `u128` from a fixed-width storage field.
pub(crate) fn decode_u128_be(bytes: &[u8], field: &'static str) -> Result<u128, CodecError> {
Ok(u128::from_be_bytes(fixed_bytes::<U128_LEN>(bytes, field)?))
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{
};

fn mk_job_id(seed: u128) -> SettlementJobId {
SettlementJobId::from(ulid::Ulid::from(seed))
SettlementJobId::from(seed)
}

fn mk_settlement_job(seed: u8) -> SettlementJob {
Expand Down
59 changes: 59 additions & 0 deletions crates/agglayer-storage/src/types/codec_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
macro_rules! codec_tests {
($value:expr $(,)?) => {
#[test]
fn codec_bolero_round_trip() {
fn check<T>(_: &T)
where
T: $crate::schema::Codec
+ std::fmt::Debug
+ PartialEq
+ 'static
+ for<'a> arbitrary::Arbitrary<'a>,
{
bolero::check!()
.with_arbitrary::<T>()
.for_each(|value: &T| {
let encoded = value.encode().expect("codec encode must succeed");
let decoded = T::decode(&encoded).expect("encoded value must decode");

assert_eq!(&decoded, value);
});
}

check(&$value);
}

#[test]
fn codec_bolero_decode_never_panics() {
fn check<T>(_: &T)
where
T: $crate::schema::Codec,
{
bolero::check!().for_each(|bytes| {
let _ = T::decode(bytes);
});
}

check(&$value);
}

#[test]
fn codec_encoding_snapshot() {
fn check<T>(value: T)
where
T: $crate::schema::Codec + std::fmt::Debug + PartialEq,
{
let encoded = value.encode().expect("codec encode must succeed");
let encoded_hex = hex::encode(&encoded);
insta::assert_snapshot!("codec_encoding_snapshot", encoded_hex);

let decoded = T::decode(&encoded).expect("encoded value must decode");
assert_eq!(decoded, value);
}

check($value);
}
};
}

pub(crate) use codec_tests;
10 changes: 4 additions & 6 deletions crates/agglayer-storage/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ pub(crate) mod disabled_network;
pub mod generated; // TODO: remove "pub" once implementation of storage is completed
pub(crate) mod network_info;
pub(crate) mod proof;
pub(crate) mod settlement;

#[cfg(test)]
pub(crate) mod codec_tests;
#[cfg(test)]
mod proto_roundtrip;
pub(crate) mod settlement;
#[cfg(any(test, feature = "testutils"))]
mod testutils;

Expand Down Expand Up @@ -74,9 +77,4 @@ crate::schema::impl_codec_using_bincode_for!(
Proof,
SmtKey,
SmtValue,
network_info::Key,
settlement::job::Key,
settlement::attempt::Key,
settlement::attempt_per_wallet::Key,
settlement::attempt_per_wallet::Value,
);
49 changes: 47 additions & 2 deletions crates/agglayer-storage/src/types/network_info/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
use std::io;

use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;

pub use super::generated::agglayer::storage::v0;
use crate::{
schema::CodecError,
schema::{Codec, CodecError},
types::network_info::v0::{
network_info_value::{self, ValueDiscriminants},
NetworkType,
},
};

#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Key {
pub(crate) network_id: u32,
pub(crate) kind: ValueDiscriminants,
}

impl Key {
pub(crate) const NETWORK_ID_LEN: usize = crate::schema::U32_LEN;
pub(crate) const LEN: usize = Self::NETWORK_ID_LEN + crate::schema::U32_LEN;

pub(crate) fn all_keys_for_network(
network_id: agglayer_types::NetworkId,
) -> impl ExactSizeIterator<Item = Self> + Clone {
Expand All @@ -27,6 +32,27 @@ impl Key {

pub type Value = super::generated::agglayer::storage::v0::NetworkInfoValue;

impl Codec for Key {
fn encode_into<W: io::Write>(&self, mut writer: W) -> Result<(), CodecError> {
writer.write_all(&self.network_id.to_be_bytes())?;
writer.write_all(&(self.kind as u32).to_be_bytes())?;

Ok(())
}

fn decode(buf: &[u8]) -> Result<Self, CodecError> {
let key = crate::schema::fixed_bytes::<{ Key::LEN }>(buf, "network info key")?;
let network_id =
crate::schema::decode_u32_be(&key[..Key::NETWORK_ID_LEN], "network info network id")?;
let kind = crate::schema::decode_u32_be(&key[Key::NETWORK_ID_LEN..], "network info kind")?;
let kind = ValueDiscriminants::from_repr(kind as usize).ok_or_else(|| {
CodecError::InvalidEnumVariant(format!("invalid network info key kind {kind}"))
})?;

Ok(Self { network_id, kind })
}
}

crate::schema::impl_codec_using_protobuf_for!(Value);

impl TryFrom<v0::NetworkType> for agglayer_types::NetworkType {
Expand All @@ -49,8 +75,22 @@ impl TryFrom<v0::NetworkType> for agglayer_types::NetworkType {
mod tests {
use strum::EnumCount;

use super::Key;
use crate::types::network_info::v0::network_info_value::ValueDiscriminants;

impl<'a> arbitrary::Arbitrary<'a> for Key {
fn arbitrary(input: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let kind_index = <u8 as arbitrary::Arbitrary>::arbitrary(input)? as usize
% ValueDiscriminants::COUNT;

Ok(Self {
network_id: <u32 as arbitrary::Arbitrary>::arbitrary(input)?,
kind: ValueDiscriminants::from_repr(kind_index)
.expect("modulo keeps discriminant in range"),
})
}
}

#[test]
fn test_discriminant_from_u32() {
assert_eq!(ValueDiscriminants::COUNT, 4, "Expected 4 discriminants");
Expand All @@ -74,4 +114,9 @@ mod tests {
}
}
}

crate::types::codec_tests::codec_tests!(Key {
network_id: 0x01020304,
kind: ValueDiscriminants::SettledClaim,
});
}
Loading
Loading