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
10 changes: 5 additions & 5 deletions crates/composable-cow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "ComposableCoW keeper machinery: the conditional-order body, the structured poll Verdict with the deployed 1.x revert decoding quarantined behind LegacyRevertAdapter, and the sweep composition over the venue client."
description = "ComposableCoW keeper machinery: the conditional-order body, the structured poll Verdict with the deployed 1.x revert decoding quarantined behind LegacyRevertAdapter, and the run composition over the venue client."

[lib]
# Plain library, keeper-side only. The CoW venue crate is orderbook-only
Expand All @@ -21,7 +21,7 @@ alloy-sol-types.workspace = true
borsh.workspace = true
cowprotocol = { version = "0.2.0", default-features = false }
nexum-sdk = { path = "../nexum-sdk" }
# `sweep` slice: the keeper run over the typed CoW client on the
# `run` slice: the keeper run over the typed CoW client on the
# `videre:venue/client` seam.
cow-venue = { path = "../cow-venue", features = ["client", "assembly"], optional = true }
videre-sdk = { path = "../videre-sdk", optional = true }
Expand All @@ -30,12 +30,12 @@ tracing = { workspace = true, optional = true }
[features]
# The poll-loop composition conditional-commitment keepers share:
# gate/journal discipline, pool submission, and retry dispatch.
sweep = ["dep:cow-venue", "dep:videre-sdk", "dep:tracing"]
run = ["dep:cow-venue", "dep:videre-sdk", "dep:tracing"]

[dev-dependencies]
proptest.workspace = true
nexum-sdk-test = { path = "../nexum-sdk-test" }

[[test]]
name = "sweep"
required-features = ["sweep"]
name = "run"
required-features = ["run"]
12 changes: 6 additions & 6 deletions crates/composable-cow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
//! ComposableCoW keeper machinery, kept out of the CoW venue: the
//! conditional-order body ([`ComposableBody`]) and the structured poll
//! seam ([`Verdict`]), with the deployed 1.x reverting wire quarantined
//! behind [`LegacyRevertAdapter`]. The `sweep` slice adds the shared
//! poll-loop composition ([`run`]) over the typed CoW venue client.
//! behind [`LegacyRevertAdapter`]. The `run` slice adds the shared
//! poll-loop composition ([`run`](run::run)) over the typed CoW venue client.

#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![warn(missing_docs)]

pub mod body;
pub mod poll;
#[cfg(feature = "sweep")]
pub mod sweep;
#[cfg(feature = "run")]
pub mod run;

pub use body::ComposableBody;
pub use poll::{IConditionalOrder, LegacyRevertAdapter, Verdict};
#[cfg(feature = "sweep")]
pub use sweep::run;
#[cfg(feature = "run")]
pub use run::run;
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
//! Keeper sweep: the poll-loop composition conditional-
//! Keeper run: the poll-loop composition conditional-
//! commitment modules share.
//!
//! [`run`] walks the keeper watch set, polls each gate-ready
//! watch through a [`ConditionalSource`], and runs the
//! watch through a [`Poller`], and runs the
//! [`Verdict`]'s effect: lifecycle outcomes update the gate and
//! watch stores, `Post` drives one submission through the typed
//! [`CowClient`] onto the `videre:venue/client` seam with the
//! `submitted:` journal as the idempotency guard - keyed on the
//! venue-and-body [`intent_id`] - and the keeper [`Retrier`]
//! as the failure dispatch.
//!
//! Store faults abort the sweep (the next tick replays it);
//! Store faults abort the run (the next tick replays it);
//! submission failures never do - they fold into a
//! [`RetryAction`] through the videre
//! [`retry_action`] table, a `denied` refusal re-entering the CoW
//! classification through its errorType prefix
//! ([`classify_denied`]) so a one-shot row survives the coarse
//! collapse, the ledger applies the effect, and the
//! sweep moves on. Diagnostics go through the guest `tracing` facade -
//! run moves on. Diagnostics go through the guest `tracing` facade -
//! the same channel strategy code logs on - so module tests observe
//! the composed behaviour with one capture.

Expand All @@ -26,9 +26,7 @@ use cow_venue::assembly::{gpv2_to_order_data, order_data_to_body};
use cow_venue::{CowClient, CowIntent, CowIntentBody, SignedOrder, classify_denied, intent_id};
use cowprotocol::GPv2OrderData;
use nexum_sdk::host::{Fault, LocalStoreHost};
use nexum_sdk::keeper::{
ConditionalSource, Gates, Journal, Retrier, RetryAction, Tick, WatchRef, WatchSet,
};
use nexum_sdk::keeper::{Gates, Journal, Poller, Retrier, RetryAction, Tick, WatchRef, WatchSet};
use std::task::Poll;

use videre_sdk::client::poll_once;
Expand All @@ -43,7 +41,7 @@ use crate::Verdict;
pub fn run<H, S, T>(host: &H, venue: &CowClient<T>, source: &S, tick: &Tick) -> Result<(), Fault>
where
H: LocalStoreHost,
S: ConditionalSource<H, Outcome = Verdict>,
S: Poller<H, Outcome = Verdict>,
T: VenueTransport,
{
let watches = WatchSet::new(host);
Expand Down Expand Up @@ -155,7 +153,7 @@ where
tracing::error!("submitted {intent_id} but refusal-marker clear failed: {fault}");
}
// The submit already succeeded; a journal-store fault here
// must not abort the sweep or unwind the accepted order.
// must not abort the run or unwind the accepted order.
// Log and carry on - the already-submitted arm keeps the
// next tick's re-post idempotent.
if let Err(fault) = journal.record(&intent_id) {
Expand All @@ -167,7 +165,7 @@ where
);
}
Ok(SubmitOutcome::RequiresSigning(_)) => {
// A sweep cannot sign; nothing is journalled, so the next
// A run cannot sign; nothing is journalled, so the next
// tick surfaces the same ask afresh.
tracing::warn!("{label} submit for {owner:#x} requires signing; not journalled");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Sweep acceptance tests: `run` over the generic store mocks with a
//! Run acceptance tests: `run` over the generic store mocks with a
//! scripted venue transport on the `videre:venue/client` seam.

use std::cell::{Cell, RefCell};
Expand All @@ -10,7 +10,7 @@ use cow_venue::assembly::{gpv2_to_order_data, order_data_to_body};
use cow_venue::{CowClient, CowIntent, CowIntentBody, CowVenue, SignedOrder};
use cowprotocol::{BuyTokenDestination, GPv2OrderData, OrderKind, SellTokenSource};
use nexum_sdk::host::LocalStoreHost as _;
use nexum_sdk::keeper::{ConditionalSource, Gates, Journal, Tick, WatchRef, WatchSet};
use nexum_sdk::keeper::{Gates, Journal, Poller, Tick, WatchRef, WatchSet};
use nexum_sdk_test::{MockHost, capture_tracing};
use videre_sdk::client::sealed::SealedTransport;
use videre_sdk::keeper::submission_key;
Expand All @@ -22,7 +22,7 @@ use videre_sdk::{
const SEPOLIA: u64 = 11_155_111;

/// Scripted venue transport: one submit outcome per queued entry,
/// every submit recorded. Quote, status, and cancel are off the sweep
/// every submit recorded. Quote, status, and cancel are off the run
/// path.
#[derive(Default)]
struct MockVenue {
Expand Down Expand Up @@ -77,7 +77,7 @@ fn client(venue: &MockVenue) -> CowClient<&MockVenue> {
/// observes its own poll calls.
struct FnSource<F>(F);

impl<H, F> ConditionalSource<H> for FnSource<F>
impl<H, F> Poller<H> for FnSource<F>
where
F: Fn(&H, WatchRef<'_>, &[u8], &Tick) -> Verdict,
{
Expand Down Expand Up @@ -144,7 +144,7 @@ fn seed_watch(host: &MockHost) -> String {
.unwrap()
}

/// The encoded intent body the sweep submits for `order`.
/// The encoded intent body the run submits for `order`.
fn intent_bytes(order: &GPv2OrderData) -> Vec<u8> {
let order_data = gpv2_to_order_data(order).expect("known markers");
CowIntentBody::V1(CowIntent::Signed(SignedOrder {
Expand All @@ -156,7 +156,7 @@ fn intent_bytes(order: &GPv2OrderData) -> Vec<u8> {
.expect("body encodes")
}

/// The intent-id the sweep journals for `order`: the venue-and-body
/// The intent-id the run journals for `order`: the venue-and-body
/// key over the same signed body `run` derives pre-submit.
fn intent_id(order: &GPv2OrderData) -> String {
submission_key(&CowVenue::ID, &intent_bytes(order))
Expand Down Expand Up @@ -406,7 +406,7 @@ fn ready_with_unknown_marker_skips_submit_and_keeps_the_watch() {
assert!(host.store.snapshot().contains_key(&key));
}

/// A sweep cannot sign: a `requires-signing` outcome is surfaced, not
/// A run cannot sign: a `requires-signing` outcome is surfaced, not
/// journalled, so the next tick re-poses the same ask.
#[test]
fn requires_signing_is_surfaced_and_not_journalled() {
Expand Down
6 changes: 3 additions & 3 deletions crates/cow-venue/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ impl Venue for CowVenue {}
/// or submit a foreign body.
pub type CowClient<T = HostVenues> = VenueClient<CowVenue, T>;

/// Deterministic intent-id for `body`: the sweep's
/// Deterministic intent-id for `body`: the run's
/// [`submission_key`] bound to [`CowVenue::ID`]. Derivable before any
/// network work, so a keeper journals the same key whether it submits
/// through the sweep or directly.
/// through the run or directly.
///
/// The key covers the encoded body, so a signed payload
/// ([`CowIntent::Signed`](crate::CowIntent::Signed)) keys on its
Expand Down Expand Up @@ -121,7 +121,7 @@ mod tests {
assert_eq!(
id,
submission_key(&CowVenue::ID, &body.to_bytes().expect("body encodes")),
"the id must be exactly the key the generic sweep journals",
"the id must be exactly the key the generic run journals",
);
assert!(id.starts_with("cow:0x"));

Expand Down
8 changes: 4 additions & 4 deletions crates/nexum-sdk/src/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//!
//! Two pieces drive the stores from the poll loop:
//!
//! - [`ConditionalSource`] - the world-neutral poll seam: one watch in,
//! - [`Poller`] - the world-neutral poll seam: one watch in,
//! one outcome out, at a given [`Tick`]. Implementations own the
//! transport and the outcome shape.
//! - [`Retrier`] - runs a [`RetryAction`]'s effect through the
Expand Down Expand Up @@ -348,8 +348,8 @@ pub struct Tick {
/// owns its own wire (an `eth_call`, an HTTP probe, a stub).
///
/// A transient failure should surface as a retry-flavoured outcome,
/// not tear down the caller's sweep: `poll` is infallible by contract.
pub trait ConditionalSource<H> {
/// not tear down the caller's run: `poll` is infallible by contract.
pub trait Poller<H> {
/// What one poll produces.
type Outcome;

Expand All @@ -362,7 +362,7 @@ pub trait ConditionalSource<H> {
/// (for example `"twap"`). Diagnostic only - no behaviour keys
/// off it.
fn label(&self) -> &'static str {
"conditional"
"poller"
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/nexum-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
//! - [`keeper`] - strategy-keeper stores over [`LocalStoreHost`]:
//! the watch-set registry ([`WatchSet`]), block/epoch gate keys
//! ([`Gates`]) and the receipt-keyed idempotency journal
//! ([`Journal`]); plus the [`ConditionalSource`] poll seam and the
//! ([`Journal`]); plus the [`Poller`] poll seam and the
//! [`Retrier`] dispatching a [`RetryAction`] through the stores.
//!
//! - [`chain`] - typed chain access: alloy [`Chain`],
Expand Down Expand Up @@ -94,7 +94,7 @@
//! [`WatchSet`]: keeper::WatchSet
//! [`Gates`]: keeper::Gates
//! [`Journal`]: keeper::Journal
//! [`ConditionalSource`]: keeper::ConditionalSource
//! [`Poller`]: keeper::Poller
//! [`Retrier`]: keeper::Retrier
//! [`RetryAction`]: keeper::RetryAction
//! [`Chain`]: alloy_chains::Chain
Expand Down
8 changes: 4 additions & 4 deletions crates/nexum-sdk/tests/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use alloy_primitives::{Address, B256, address, b256};
use nexum_sdk::host::{Fault, LocalStoreHost as _};
use nexum_sdk::keeper::{
ConditionalSource, Gates, Journal, NEXT_BLOCK_PREFIX, NEXT_EPOCH_PREFIX, REFUSED_PREFIX,
Retrier, RetryAction, Tick, WATCH_PREFIX, WatchRef, WatchSet, watch_key,
Gates, Journal, NEXT_BLOCK_PREFIX, NEXT_EPOCH_PREFIX, Poller, REFUSED_PREFIX, Retrier,
RetryAction, Tick, WATCH_PREFIX, WatchRef, WatchSet, watch_key,
};
use nexum_sdk_test::MockHost;

Expand Down Expand Up @@ -559,9 +559,9 @@ fn retry_action_labels_are_stable_snake_case() {
/// keeper passes the stored params verbatim and the tick it judged
/// the gates by.
#[test]
fn conditional_source_sees_params_and_tick_verbatim() {
fn poller_sees_params_and_tick_verbatim() {
struct EchoSource;
impl<H> ConditionalSource<H> for EchoSource {
impl<H> Poller<H> for EchoSource {
type Outcome = (usize, u64, u64, u64, String);
fn poll(
&self,
Expand Down
2 changes: 1 addition & 1 deletion crates/videre-host/tests/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ async fn e2e_twap_monitor_boots_against_the_cow_adapter() {
assert_eq!(supervisor.alive_count(), 1, "twap-monitor is alive");

// twap-monitor subscribes to Sepolia blocks (poll path); with no
// watches indexed the sweep is empty and the keeper stays alive.
// watches indexed the run is empty and the keeper stays alive.
assert_eq!(supervisor.dispatch_block(block(11_155_111)).await, 1);
assert_eq!(supervisor.alive_count(), 1);
}
Expand Down
6 changes: 3 additions & 3 deletions crates/videre-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Guest-side videre SDK: the VenueAdapter trait mirroring the venue-adapter world, the borsh-versioned IntentBody codec, the typed venue client over the native-AFIT transport seam, the generic keeper sweep assembler, and typed wrappers over the scoped transport imports."
description = "Guest-side videre SDK: the VenueAdapter trait mirroring the venue-adapter world, the borsh-versioned IntentBody codec, the typed venue client over the native-AFIT transport seam, the generic keeper run assembler, and typed wrappers over the scoped transport imports."

[lib]
# Plain library - adapters link this and emit their own cdylib for the
Expand Down Expand Up @@ -36,10 +36,10 @@ videre-status-body = { path = "../videre-status-body" }
http.workspace = true
strum.workspace = true
thiserror.workspace = true
# Best-effort fault logs on the sweep's non-critical store cleanups.
# Best-effort fault logs on the run's non-critical store cleanups.
tracing.workspace = true
wit-bindgen.workspace = true

[dev-dependencies]
# In-memory `LocalStoreHost` behind the keeper sweep tests.
# In-memory `LocalStoreHost` behind the keeper run tests.
nexum-sdk-test = { path = "../nexum-sdk-test" }
Loading
Loading