diff --git a/Cargo.lock b/Cargo.lock index 648f87837..6a74d8492 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -763,6 +763,7 @@ dependencies = [ "sp1-sdk 6.2.1", "strum_macros 0.28.0", "thiserror 2.0.18", + "ulid", "unified-bridge 0.17.0", ] diff --git a/crates/agglayer-settlement-service/src/settlement_service.rs b/crates/agglayer-settlement-service/src/settlement_service.rs index 6c4f81935..a88342198 100644 --- a/crates/agglayer-settlement-service/src/settlement_service.rs +++ b/crates/agglayer-settlement-service/src/settlement_service.rs @@ -2,14 +2,13 @@ use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; use agglayer_config::settlement_service::SettlementServiceConfig; use agglayer_storage::stores::{SettlementReader, SettlementWriter}; -use agglayer_types::{SettlementJob, SettlementJobResult}; +use agglayer_types::{SettlementJob, SettlementJobId, SettlementJobResult}; use alloy::providers::Provider; use educe::Educe; use eyre::Context as _; use tokio::sync::{mpsc, watch, Mutex}; use tokio_util::sync::CancellationToken; use tracing::{error, info}; -use ulid::Ulid; use crate::settlement_task::{ SettlementTask, SettlementTaskRunResult, TaskAdminCommand, TaskControlHandle, @@ -25,13 +24,14 @@ pub struct SettlementService { provider: Arc, store: Arc, cancellation_token: CancellationToken, - task_controls: Arc>>, - result_watchers: Arc>>>>, + task_controls: Arc>>, + result_watchers: + Arc>>>>, } pub struct SettlementJobWatcher { watcher: watch::Receiver>, - job_id: Ulid, + job_id: SettlementJobId, } impl SettlementJobWatcher { @@ -39,7 +39,7 @@ impl SettlementJobWatcher { &mut self.watcher } - pub fn job_id(&self) -> Ulid { + pub fn job_id(&self) -> SettlementJobId { self.job_id } } @@ -72,7 +72,7 @@ impl< } #[tracing::instrument(skip_all)] - async fn task_control(&self, job_id: Ulid) -> eyre::Result { + async fn task_control(&self, job_id: SettlementJobId) -> eyre::Result { let task_controls = self.task_controls.lock().await; let Some(task_control) = task_controls.get(&job_id) else { eyre::bail!("No task control found for settlement task {job_id}"); @@ -81,7 +81,11 @@ impl< } #[tracing::instrument(skip_all)] - async fn admin_task(&self, job_id: Ulid, command: TaskAdminCommand) -> eyre::Result<()> { + async fn admin_task( + &self, + job_id: SettlementJobId, + command: TaskAdminCommand, + ) -> eyre::Result<()> { self.task_control(job_id) .await? .try_send(command) @@ -95,13 +99,13 @@ impl< } #[tracing::instrument(skip_all)] - pub async fn admin_abort_task(&self, job_id: Ulid) -> eyre::Result<()> { + pub async fn admin_abort_task(&self, job_id: SettlementJobId) -> eyre::Result<()> { self.task_control(job_id).await?.cancel(); Ok(()) } #[tracing::instrument(skip_all)] - pub async fn admin_reload_and_restart_task(&self, job_id: Ulid) -> eyre::Result<()> { + pub async fn admin_reload_and_restart_task(&self, job_id: SettlementJobId) -> eyre::Result<()> { self.admin_task(job_id, TaskAdminCommand::ReloadAndRestart) .await } @@ -160,7 +164,7 @@ impl< #[tracing::instrument(skip(self))] pub async fn retrieve_settlement_result( &self, - job_id: Ulid, + job_id: SettlementJobId, ) -> eyre::Result { if let Some(watcher) = self.result_watchers.lock().await.get(&job_id) { return match watcher.borrow().as_ref() { @@ -200,7 +204,7 @@ impl< } } -pub struct RetrieveSettlementResult(pub Ulid); +pub struct RetrieveSettlementResult(pub SettlementJobId); impl< L1Provider: Provider + 'static, @@ -225,8 +229,8 @@ impl< } pub enum AdminCommand { - AbortTask(Ulid), - ReloadAndRestartTask(Ulid), + AbortTask(SettlementJobId), + ReloadAndRestartTask(SettlementJobId), } impl< diff --git a/crates/agglayer-settlement-service/src/settlement_task.rs b/crates/agglayer-settlement-service/src/settlement_task.rs index 467d67690..9449f4f46 100644 --- a/crates/agglayer-settlement-service/src/settlement_task.rs +++ b/crates/agglayer-settlement-service/src/settlement_task.rs @@ -9,7 +9,7 @@ use agglayer_storage::stores::{SettlementReader, SettlementWriter}; use agglayer_types::{ ClientError, ClientErrorType, ContractCallOutcome, ContractCallResult, Digest, Nonce, SettlementAttempt, SettlementAttemptNumber, SettlementAttemptResult, SettlementJob, - SettlementJobResult, SettlementTxHash, + SettlementJobId, SettlementJobResult, SettlementTxHash, }; use alloy::{ consensus::{EthereumTxEnvelope, TxEip4844Variant}, @@ -20,7 +20,6 @@ use alloy::{ use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use tracing::{error, warn}; -use ulid::Ulid; use crate::utils::RetryCallbackError; @@ -32,7 +31,7 @@ type TxEnvelope = EthereumTxEnvelope; {error_message}" )] struct NonRecoverableError { - settlement_task_id: Ulid, + settlement_task_id: SettlementJobId, file: &'static str, line: u32, error_message: String, @@ -105,7 +104,7 @@ struct ActiveSettlementAttempt { } pub struct SettlementTask { - id: Ulid, + id: SettlementJobId, job: SettlementJob, provider: Arc, store: Arc, @@ -124,7 +123,7 @@ impl, store: Arc, control: TaskControl, - ) -> eyre::Result<(Ulid, Self)> { + ) -> eyre::Result<(SettlementJobId, Self)> { let id = loop { if let Ok(id) = ID_GENERATOR .get_or_init(|| std::sync::Mutex::new(ulid::Generator::new())) @@ -132,7 +131,7 @@ impl, store: Arc, control: TaskControl, @@ -535,7 +534,7 @@ impl eyre::Result<(SettlementJob, Option)> { // TODO: Load a settlement job's contents from DB, including its // result if it is completed. diff --git a/crates/agglayer-storage/src/columns/settlement_attempt_results/tests.rs b/crates/agglayer-storage/src/columns/settlement_attempt_results/tests.rs index c33729ebc..45d0d9285 100644 --- a/crates/agglayer-storage/src/columns/settlement_attempt_results/tests.rs +++ b/crates/agglayer-storage/src/columns/settlement_attempt_results/tests.rs @@ -1,4 +1,4 @@ -use ulid::Ulid; +use agglayer_types::SettlementJobId; use crate::{ schema::Codec as _, @@ -14,7 +14,7 @@ use crate::{ #[test] fn settlement_attempt_result_roundtrip_codec() { let key = attempt::Key { - settlement_job_id: Ulid::from(7u128), + settlement_job_id: SettlementJobId::from(ulid::Ulid::from(7u128)), attempt_sequence_number: 3, }; let value = SettlementAttemptResult::contract_call_success_for_test(23); diff --git a/crates/agglayer-storage/src/columns/settlement_attempts/tests.rs b/crates/agglayer-storage/src/columns/settlement_attempts/tests.rs index d4892dd71..2fcb6dabf 100644 --- a/crates/agglayer-storage/src/columns/settlement_attempts/tests.rs +++ b/crates/agglayer-storage/src/columns/settlement_attempts/tests.rs @@ -1,5 +1,5 @@ +use agglayer_types::SettlementJobId; use rocksdb::{Direction, ReadOptions}; -use ulid::Ulid; use super::SettlementAttemptsColumn; use crate::{ @@ -15,7 +15,7 @@ use crate::{ #[test] fn settlement_attempt_roundtrip_codec() { let key = Key { - settlement_job_id: Ulid::from(24u128), + settlement_job_id: SettlementJobId::from(ulid::Ulid::from(24u128)), attempt_sequence_number: 2, }; let value = mk_settlement_attempt(2); @@ -38,7 +38,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 = Ulid::from(99u128); + let settlement_job_id = SettlementJobId::from(ulid::Ulid::from(99u128)); for seq in [1u64, 2, 3, 4, 5] { let key = Key { settlement_job_id, diff --git a/crates/agglayer-storage/src/columns/settlement_job_results/tests.rs b/crates/agglayer-storage/src/columns/settlement_job_results/tests.rs index d3fb64b6c..2407dd519 100644 --- a/crates/agglayer-storage/src/columns/settlement_job_results/tests.rs +++ b/crates/agglayer-storage/src/columns/settlement_job_results/tests.rs @@ -1,4 +1,4 @@ -use ulid::Ulid; +use agglayer_types::SettlementJobId; use crate::{ schema::Codec as _, @@ -10,7 +10,7 @@ use crate::{ #[test] fn settlement_job_result_roundtrip_codec() { - let key = Ulid::from(7u128); + let key = SettlementJobId::from(ulid::Ulid::from(7u128)); let value = SettlementJobResult::contract_call_success_for_test(23); let encoded_key = key.encode().expect("Unable to encode key"); diff --git a/crates/agglayer-storage/src/columns/settlement_jobs/tests.rs b/crates/agglayer-storage/src/columns/settlement_jobs/tests.rs index 562765120..7296db087 100644 --- a/crates/agglayer-storage/src/columns/settlement_jobs/tests.rs +++ b/crates/agglayer-storage/src/columns/settlement_jobs/tests.rs @@ -1,4 +1,4 @@ -use ulid::Ulid; +use agglayer_types::SettlementJobId; use crate::{ schema::Codec as _, @@ -12,7 +12,7 @@ use crate::{ #[test] fn settlement_job_roundtrip_codec() { - let key = Ulid::from(42u128); + let key = SettlementJobId::from(ulid::Ulid::from(42u128)); let value = mk_settlement_job(); let encoded_key = key.encode().expect("Unable to encode key"); diff --git a/crates/agglayer-storage/src/stores/interfaces/reader/settlement_reader.rs b/crates/agglayer-storage/src/stores/interfaces/reader/settlement_reader.rs index 2f47126d8..814999707 100644 --- a/crates/agglayer-storage/src/stores/interfaces/reader/settlement_reader.rs +++ b/crates/agglayer-storage/src/stores/interfaces/reader/settlement_reader.rs @@ -1,7 +1,6 @@ use agglayer_types::{ - SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobResult, + SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobId, SettlementJobResult, }; -use ulid::Ulid; use crate::error::Error; @@ -11,23 +10,26 @@ use crate::error::Error; /// list reads return an empty vector when no records are found. pub trait SettlementReader: Send + Sync { /// Returns the settlement job for `settlement_job_id`, if present. - fn get_settlement_job(&self, settlement_job_id: &Ulid) -> Result, Error>; + fn get_settlement_job( + &self, + settlement_job_id: &SettlementJobId, + ) -> Result, Error>; /// Returns the terminal result for `settlement_job_id`, if present. fn get_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; /// Returns all settlement attempts recorded for `settlement_job_id`. fn list_settlement_attempts( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; /// Returns all settlement attempt results recorded for `settlement_job_id`. fn list_settlement_attempt_results( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; } diff --git a/crates/agglayer-storage/src/stores/interfaces/writer/settlement_writer.rs b/crates/agglayer-storage/src/stores/interfaces/writer/settlement_writer.rs index b70fe4696..13849d5c3 100644 --- a/crates/agglayer-storage/src/stores/interfaces/writer/settlement_writer.rs +++ b/crates/agglayer-storage/src/stores/interfaces/writer/settlement_writer.rs @@ -1,7 +1,6 @@ use agglayer_types::{ - SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobResult, + SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobId, SettlementJobResult, }; -use ulid::Ulid; use crate::error::Error; @@ -16,7 +15,7 @@ pub trait SettlementWriter: Send + Sync { /// `settlement_job_id` already exists. fn insert_settlement_job( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, settlement_job: &SettlementJob, ) -> Result<(), Error>; @@ -27,7 +26,7 @@ pub trait SettlementWriter: Send + Sync { /// job must already exist. fn insert_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, tx_result: &SettlementJobResult, ) -> Result<(), Error>; @@ -38,7 +37,7 @@ pub trait SettlementWriter: Send + Sync { /// already exists. The parent settlement job must already exist. fn insert_settlement_attempt( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, settlement_attempt: &SettlementAttempt, ) -> Result<(), Error>; @@ -50,7 +49,7 @@ pub trait SettlementWriter: Send + Sync { /// already exists. The corresponding settlement attempt must already exist. fn insert_settlement_attempt_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, tx_result: &SettlementAttemptResult, ) -> Result<(), Error>; diff --git a/crates/agglayer-storage/src/stores/state/mod.rs b/crates/agglayer-storage/src/stores/state/mod.rs index 5605428ca..07dab94be 100644 --- a/crates/agglayer-storage/src/stores/state/mod.rs +++ b/crates/agglayer-storage/src/stores/state/mod.rs @@ -8,7 +8,8 @@ use std::{ use agglayer_tries::{node::Node, smt::Smt}; use agglayer_types::{ primitives::Digest, Certificate, CertificateHeader, CertificateId, CertificateIndex, - CertificateStatus, EpochNumber, Height, LocalNetworkStateData, NetworkId, SettlementTxHash, + CertificateStatus, EpochNumber, Height, LocalNetworkStateData, NetworkId, SettlementJobId, + SettlementTxHash, }; use pessimistic_proof::{ local_balance_tree::LOCAL_BALANCE_TREE_DEPTH, nullifier_tree::NULLIFIER_TREE_DEPTH, @@ -50,7 +51,7 @@ mod tests; pub struct StateStore { db: Arc, backup_client: BackupClient, - settlement_write_locks: Mutex>>>, + settlement_write_locks: Mutex>>>, } impl StateStore { diff --git a/crates/agglayer-storage/src/stores/state/settlement/mod.rs b/crates/agglayer-storage/src/stores/state/settlement/mod.rs index 9a6b606a9..b81c89946 100644 --- a/crates/agglayer-storage/src/stores/state/settlement/mod.rs +++ b/crates/agglayer-storage/src/stores/state/settlement/mod.rs @@ -1,8 +1,7 @@ use agglayer_types::{ - SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobResult, + SettlementAttempt, SettlementAttemptResult, SettlementJob, SettlementJobId, SettlementJobResult, }; use rocksdb::{Direction, ReadOptions, WriteBatch}; -use ulid::Ulid; use super::StateStore; use crate::{ @@ -23,7 +22,7 @@ use crate::{ impl StateStore { fn with_settlement_write_lock( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, callback: impl FnOnce() -> Result, ) -> Result { let key_lock = { @@ -48,7 +47,10 @@ impl StateStore { } impl SettlementReader for StateStore { - fn get_settlement_job(&self, settlement_job_id: &Ulid) -> Result, Error> { + fn get_settlement_job( + &self, + settlement_job_id: &SettlementJobId, + ) -> Result, Error> { Ok(self .db .get::(settlement_job_id)? @@ -58,7 +60,7 @@ impl SettlementReader for StateStore { fn get_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error> { Ok(self .db @@ -69,7 +71,7 @@ impl SettlementReader for StateStore { fn list_settlement_attempts( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error> { let mut iterator = self.db.iter_with_direction::( ReadOptions::default(), @@ -98,7 +100,7 @@ impl SettlementReader for StateStore { fn list_settlement_attempt_results( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error> { let mut iterator = self .db @@ -133,7 +135,7 @@ impl SettlementReader for StateStore { impl SettlementWriter for StateStore { fn insert_settlement_job( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, settlement_job: &SettlementJob, ) -> Result<(), Error> { let settlement_job: v0::SettlementJob = settlement_job.into(); @@ -157,7 +159,7 @@ impl SettlementWriter for StateStore { fn insert_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, tx_result: &SettlementJobResult, ) -> Result<(), Error> { let tx_result: v0::SettlementJobResult = tx_result.into(); @@ -191,7 +193,7 @@ impl SettlementWriter for StateStore { fn insert_settlement_attempt( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, settlement_attempt: &SettlementAttempt, ) -> Result<(), Error> { @@ -253,7 +255,7 @@ impl SettlementWriter for StateStore { fn insert_settlement_attempt_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, tx_result: &SettlementAttemptResult, ) -> Result<(), Error> { diff --git a/crates/agglayer-storage/src/stores/state/tests/settlement.rs b/crates/agglayer-storage/src/stores/state/tests/settlement.rs index ea39454b3..3e32645dd 100644 --- a/crates/agglayer-storage/src/stores/state/tests/settlement.rs +++ b/crates/agglayer-storage/src/stores/state/tests/settlement.rs @@ -4,9 +4,9 @@ use std::{ }; use agglayer_types::{ - Address, Digest, Nonce, SettlementAttempt, SettlementJob, SettlementTxHash, U256, + Address, Digest, Nonce, SettlementAttempt, SettlementJob, SettlementJobId, SettlementTxHash, + U256, }; -use ulid::Ulid; use crate::{ backup::BackupClient, @@ -30,8 +30,8 @@ use crate::{ }, }; -fn mk_ulid(seed: u128) -> Ulid { - Ulid::from(seed) +fn mk_job_id(seed: u128) -> SettlementJobId { + SettlementJobId::from(ulid::Ulid::from(seed)) } fn mk_settlement_job(seed: u8) -> SettlementJob { @@ -71,14 +71,14 @@ fn setup_store() -> (TempDBDir, Arc, StateStore) { fn insert_settlement_job_succeeds_once() { let (_tmp, _db, store) = setup_store(); assert!(store - .insert_settlement_job(&mk_ulid(1), &mk_settlement_job(1)) + .insert_settlement_job(&mk_job_id(1), &mk_settlement_job(1)) .is_ok()); } #[test] fn insert_settlement_job_duplicate_fails() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(2); + let job_id = mk_job_id(2); let first = mk_settlement_job(2); let second = mk_settlement_job(3); store @@ -96,7 +96,7 @@ fn insert_settlement_job_duplicate_fails() { #[test] fn insert_settlement_attempt_succeeds_once() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(3); + let job_id = mk_job_id(3); store .insert_settlement_job(&job_id, &mk_settlement_job(3)) .expect("job insert must succeed"); @@ -108,7 +108,7 @@ fn insert_settlement_attempt_succeeds_once() { #[test] fn insert_settlement_attempt_duplicate_fails() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(4); + let job_id = mk_job_id(4); let first = mk_settlement_attempt(1); let second = mk_settlement_attempt(2); store @@ -132,14 +132,14 @@ fn insert_settlement_attempt_duplicate_fails() { #[test] fn insert_settlement_attempt_without_job_fails() { let (_tmp, _db, store) = setup_store(); - let res = store.insert_settlement_attempt(&mk_ulid(404), 1, &mk_settlement_attempt(1)); + let res = store.insert_settlement_attempt(&mk_job_id(404), 1, &mk_settlement_attempt(1)); assert!(matches!(res, Err(Error::UnprocessedAction(_)))); } #[test] fn insert_settlement_attempt_result_succeeds_once() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(5); + let job_id = mk_job_id(5); store .insert_settlement_job(&job_id, &mk_settlement_job(5)) .expect("job insert must succeed"); @@ -160,7 +160,7 @@ fn insert_settlement_attempt_result_succeeds_once() { #[test] fn insert_settlement_attempt_result_duplicate_fails() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(6); + let job_id = mk_job_id(6); let first = v0::SettlementAttemptResult::contract_call_success_for_test(1) .try_into() .expect("test tx result helper should be decodable"); @@ -191,7 +191,7 @@ fn insert_settlement_attempt_result_duplicate_fails() { #[test] fn insert_settlement_attempt_result_without_attempt_fails() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(405); + let job_id = mk_job_id(405); store .insert_settlement_job(&job_id, &mk_settlement_job(42)) .expect("job insert must succeed"); @@ -209,7 +209,7 @@ fn insert_settlement_attempt_result_without_attempt_fails() { #[test] fn insert_settlement_attempt_indexes_by_wallet_and_nonce() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(406); + let job_id = mk_job_id(406); let seq = 3; let attempt = mk_settlement_attempt(seq); let wallet_bytes = attempt.sender_wallet.into_array(); @@ -241,7 +241,7 @@ fn get_settlement_job_returns_none_when_missing() { let (_tmp, _db, store) = setup_store(); assert_eq!( store - .get_settlement_job(&mk_ulid(10)) + .get_settlement_job(&mk_job_id(10)) .expect("read must succeed"), None ); @@ -250,7 +250,7 @@ fn get_settlement_job_returns_none_when_missing() { #[test] fn get_settlement_job_returns_value_after_insert() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(11); + let job_id = mk_job_id(11); let job = mk_settlement_job(11); store .insert_settlement_job(&job_id, &job) @@ -268,7 +268,7 @@ fn get_settlement_job_result_returns_none_when_missing() { let (_tmp, _db, store) = setup_store(); assert_eq!( store - .get_settlement_job_result(&mk_ulid(12)) + .get_settlement_job_result(&mk_job_id(12)) .expect("read must succeed"), None ); @@ -278,7 +278,7 @@ fn get_settlement_job_result_returns_none_when_missing() { fn insert_settlement_job_result_without_job_fails() { let (_tmp, _db, store) = setup_store(); let res = store.insert_settlement_job_result( - &mk_ulid(13), + &mk_job_id(13), &v0::SettlementJobResult::contract_call_success_for_test(13) .try_into() .expect("test tx result helper should be decodable"), @@ -289,7 +289,7 @@ fn insert_settlement_job_result_without_job_fails() { #[test] fn get_settlement_job_result_returns_value_after_insert() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(14); + let job_id = mk_job_id(14); let result = v0::SettlementJobResult::contract_call_success_for_test(14) .try_into() .expect("test tx result helper should be decodable"); @@ -314,7 +314,7 @@ fn list_settlement_attempts_returns_empty_vec_for_missing_job() { let (_tmp, _db, store) = setup_store(); assert!(store - .list_settlement_attempts(&mk_ulid(16)) + .list_settlement_attempts(&mk_job_id(16)) .expect("read must succeed") .is_empty()); } @@ -322,7 +322,7 @@ fn list_settlement_attempts_returns_empty_vec_for_missing_job() { #[test] fn list_settlement_attempts_returns_all_attempts_for_job() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(17); + let job_id = mk_job_id(17); let first = mk_settlement_attempt(1); let second = mk_settlement_attempt(2); let third = mk_settlement_attempt(3); @@ -351,8 +351,8 @@ fn list_settlement_attempts_returns_all_attempts_for_job() { #[test] fn list_settlement_attempts_does_not_return_attempts_from_other_jobs() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(18); - let other_job_id = mk_ulid(19); + let job_id = mk_job_id(18); + let other_job_id = mk_job_id(19); let first = mk_settlement_attempt(1); let second = mk_settlement_attempt(2); @@ -385,7 +385,7 @@ fn list_settlement_attempt_results_returns_empty_vec_for_missing_job() { let (_tmp, _db, store) = setup_store(); assert!(store - .list_settlement_attempt_results(&mk_ulid(20)) + .list_settlement_attempt_results(&mk_job_id(20)) .expect("read must succeed") .is_empty()); } @@ -393,7 +393,7 @@ fn list_settlement_attempt_results_returns_empty_vec_for_missing_job() { #[test] fn list_settlement_attempt_results_returns_all_results_for_job() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(24); + let job_id = mk_job_id(24); let first_attempt = mk_settlement_attempt(1); let second_attempt = mk_settlement_attempt(2); let first_result = v0::SettlementAttemptResult::contract_call_success_for_test(1) @@ -430,8 +430,8 @@ fn list_settlement_attempt_results_returns_all_results_for_job() { #[test] fn list_settlement_attempt_results_does_not_return_results_from_other_jobs() { let (_tmp, _db, store) = setup_store(); - let job_id = mk_ulid(25); - let other_job_id = mk_ulid(26); + let job_id = mk_job_id(25); + let other_job_id = mk_job_id(26); let first_result = v0::SettlementAttemptResult::contract_call_success_for_test(3) .try_into() .expect("test tx result helper should be decodable"); @@ -481,7 +481,7 @@ fn list_settlement_attempt_results_does_not_return_results_from_other_jobs() { #[test] fn insert_settlement_job_result_duplicate_fails() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(15); + let job_id = mk_job_id(15); let first = v0::SettlementJobResult::contract_call_success_for_test(15) .try_into() .expect("test tx result helper should be decodable"); @@ -509,7 +509,7 @@ fn insert_settlement_job_result_duplicate_fails() { #[test] fn job_attempt_result_can_be_read_back_together() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(21); + let job_id = mk_job_id(21); let job = mk_settlement_job(21); let attempt = mk_settlement_attempt(5); let attempt_result = v0::SettlementAttemptResult::contract_call_success_for_test(21) @@ -558,7 +558,7 @@ fn job_attempt_result_can_be_read_back_together() { #[test] fn result_absent_does_not_imply_attempt_absent() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(22); + let job_id = mk_job_id(22); let attempt = mk_settlement_attempt(1); store .insert_settlement_job(&job_id, &mk_settlement_job(22)) @@ -588,7 +588,7 @@ fn result_absent_does_not_imply_attempt_absent() { #[test] fn duplicate_insert_preserves_original_value() { let (_tmp, db, store) = setup_store(); - let job_id = mk_ulid(23); + let job_id = mk_job_id(23); let first = v0::SettlementAttemptResult::contract_call_success_for_test(1) .try_into() .expect("test tx result helper should be decodable"); diff --git a/crates/agglayer-storage/src/tests/mocks/state_store.rs b/crates/agglayer-storage/src/tests/mocks/state_store.rs index 5d12f2641..e3d41de18 100644 --- a/crates/agglayer-storage/src/tests/mocks/state_store.rs +++ b/crates/agglayer-storage/src/tests/mocks/state_store.rs @@ -1,10 +1,9 @@ use agglayer_types::{ primitives::Digest, Certificate, CertificateHeader, CertificateId, CertificateStatus, EpochNumber, Height, LocalNetworkStateData, NetworkId, SettlementAttempt, - SettlementAttemptResult, SettlementJob, SettlementJobResult, SettlementTxHash, + SettlementAttemptResult, SettlementJob, SettlementJobId, SettlementJobResult, SettlementTxHash, }; use mockall::mock; -use ulid::Ulid; use crate::{ columns::latest_settled_certificate_per_network::SettledCertificate, @@ -123,48 +122,48 @@ mock! { impl SettlementReader for StateStore { fn get_settlement_job( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; fn get_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; fn list_settlement_attempts( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; fn list_settlement_attempt_results( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, ) -> Result, Error>; } impl SettlementWriter for StateStore { fn insert_settlement_job( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, settlement_job: &SettlementJob, ) -> Result<(), Error>; fn insert_settlement_job_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, tx_result: &SettlementJobResult, ) -> Result<(), Error>; fn insert_settlement_attempt( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, settlement_attempt: &SettlementAttempt, ) -> Result<(), Error>; fn insert_settlement_attempt_result( &self, - settlement_job_id: &Ulid, + settlement_job_id: &SettlementJobId, attempt_sequence_number: u64, tx_result: &SettlementAttemptResult, ) -> Result<(), Error>; diff --git a/crates/agglayer-storage/src/types/settlement/attempt.rs b/crates/agglayer-storage/src/types/settlement/attempt.rs index 676103812..900921075 100644 --- a/crates/agglayer-storage/src/types/settlement/attempt.rs +++ b/crates/agglayer-storage/src/types/settlement/attempt.rs @@ -1,8 +1,9 @@ +use agglayer_types::SettlementJobId; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] pub struct Key { - pub(crate) settlement_job_id: ulid::Ulid, + pub(crate) settlement_job_id: SettlementJobId, pub(crate) attempt_sequence_number: u64, } diff --git a/crates/agglayer-storage/src/types/settlement/attempt_per_wallet.rs b/crates/agglayer-storage/src/types/settlement/attempt_per_wallet.rs index f9691977c..b2dcedaca 100644 --- a/crates/agglayer-storage/src/types/settlement/attempt_per_wallet.rs +++ b/crates/agglayer-storage/src/types/settlement/attempt_per_wallet.rs @@ -1,10 +1,11 @@ +use agglayer_types::SettlementJobId; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] pub struct Key { pub(crate) address: [u8; 20], pub(crate) nonce: u64, - pub(crate) settlement_job_id: ulid::Ulid, + pub(crate) settlement_job_id: SettlementJobId, pub(crate) attempt_sequence_number: u64, } diff --git a/crates/agglayer-storage/src/types/settlement/job.rs b/crates/agglayer-storage/src/types/settlement/job.rs index e2b656b0d..9f26adc78 100644 --- a/crates/agglayer-storage/src/types/settlement/job.rs +++ b/crates/agglayer-storage/src/types/settlement/job.rs @@ -1,4 +1,6 @@ -pub type Key = ulid::Ulid; +use agglayer_types::SettlementJobId; + +pub type Key = SettlementJobId; pub type Value = crate::types::generated::agglayer::storage::v0::SettlementJob; crate::schema::impl_codec_using_protobuf_for!(Value); diff --git a/crates/agglayer-types/Cargo.toml b/crates/agglayer-types/Cargo.toml index f82750647..6bbcdc524 100644 --- a/crates/agglayer-types/Cargo.toml +++ b/crates/agglayer-types/Cargo.toml @@ -39,6 +39,7 @@ sp1-prover.workspace = true thiserror.workspace = true rand.workspace = true strum_macros.workspace = true +ulid = { workspace = true, features = ["serde"] } [dev-dependencies] agglayer-types = { path = ".", features = ["testutils"] } diff --git a/crates/agglayer-types/src/lib.rs b/crates/agglayer-types/src/lib.rs index 0149f75e8..47d16df76 100644 --- a/crates/agglayer-types/src/lib.rs +++ b/crates/agglayer-types/src/lib.rs @@ -31,5 +31,5 @@ pub use proof_modes::{ExecutionMode, GenerationType}; pub use settlement::{ ClientError, ClientErrorType, ContractCallOutcome, ContractCallResult, Nonce, SettlementAttempt, SettlementAttemptNumber, SettlementAttemptResult, SettlementJob, - SettlementJobResult, + SettlementJobId, SettlementJobResult, }; diff --git a/crates/agglayer-types/src/settlement.rs b/crates/agglayer-types/src/settlement.rs index 9d8b54b2f..b8cdfaf73 100644 --- a/crates/agglayer-types/src/settlement.rs +++ b/crates/agglayer-types/src/settlement.rs @@ -4,6 +4,38 @@ use alloy::primitives::Bytes; use crate::{Address, SettlementTxHash, B256, U256}; +#[derive( + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + derive_more::Display, + derive_more::From, + derive_more::Into, + serde::Deserialize, + serde::Serialize, +)] +#[serde(transparent)] +pub struct SettlementJobId(ulid::Ulid); + +impl SettlementJobId { + pub const fn new(value: ulid::Ulid) -> Self { + Self(value) + } + + pub const fn as_ulid(&self) -> &ulid::Ulid { + &self.0 + } + + pub const fn into_ulid(self) -> ulid::Ulid { + self.0 + } +} + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, derive_more::Display)] pub struct SettlementAttemptNumber(pub u64);