Skip to content

Commit abe19c9

Browse files
committed
wip
1 parent 1c28e49 commit abe19c9

18 files changed

Lines changed: 708 additions & 242 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/autopilot/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ tokio-stream = { workspace = true }
6363
tower = { workspace = true }
6464
tower-http = { workspace = true, features = ["trace"] }
6565
tracing = { workspace = true }
66-
url = { workspace = true }
66+
url = { workspace = true, features = ["serde"] }
6767
winner-selection = { workspace = true }
68+
toml = { workspace = true }
6869

6970
[dev-dependencies]
7071
maplit = { workspace = true }

crates/autopilot/src/arguments.rs

Lines changed: 12 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
use {
22
crate::{database::INSERT_BATCH_SIZE_DEFAULT, infra},
3-
alloy::primitives::{Address, U256},
4-
anyhow::{Context, anyhow, ensure},
3+
alloy::primitives::Address,
4+
anyhow::Context,
55
chrono::{DateTime, Utc},
66
clap::ValueEnum,
77
shared::{
8-
arguments::{FeeFactor, display_list, display_option, display_secret_option},
8+
arguments::{FeeFactor, display_option, display_secret_option},
99
bad_token::token_owner_finder,
1010
http_client,
1111
price_estimation::{self, NativePriceEstimators},
1212
},
13-
std::{
14-
fmt::{self, Display, Formatter},
15-
net::SocketAddr,
16-
num::NonZeroUsize,
17-
str::FromStr,
18-
time::Duration,
19-
},
13+
std::{net::SocketAddr, num::NonZeroUsize, path::PathBuf, str::FromStr, time::Duration},
2014
url::Url,
2115
};
2216

2317
#[derive(clap::Parser)]
24-
pub struct Arguments {
18+
pub struct CliArguments {
19+
#[clap(long)]
20+
pub config: Option<PathBuf>,
21+
2522
#[clap(flatten)]
2623
pub shared: shared::arguments::Arguments,
2724

@@ -159,11 +156,6 @@ pub struct Arguments {
159156
)]
160157
pub trusted_tokens_update_interval: Duration,
161158

162-
/// A list of drivers in the following format:
163-
/// `<NAME>|<URL>|<SUBMISSION_ADDRESS>|<FAIRNESS_THRESHOLD>`
164-
#[clap(long, env, use_value_delimiter = true)]
165-
pub drivers: Vec<Solver>,
166-
167159
/// The maximum number of blocks to wait for a settlement to appear on
168160
/// chain.
169161
#[clap(long, env, default_value = "5")]
@@ -292,9 +284,10 @@ pub struct Arguments {
292284
pub native_price_prefetch_time: Duration,
293285
}
294286

295-
impl std::fmt::Display for Arguments {
287+
impl std::fmt::Display for CliArguments {
296288
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297289
let Self {
290+
config,
298291
shared,
299292
order_quoting,
300293
http_client,
@@ -319,7 +312,6 @@ impl std::fmt::Display for Arguments {
319312
trusted_tokens_url,
320313
trusted_tokens,
321314
trusted_tokens_update_interval,
322-
drivers,
323315
submission_deadline,
324316
shadow,
325317
solve_deadline,
@@ -343,7 +335,7 @@ impl std::fmt::Display for Arguments {
343335
native_price_cache_refresh,
344336
native_price_prefetch_time,
345337
} = self;
346-
338+
write!(f, "{}", config.clone().unwrap_or_default().display())?;
347339
write!(f, "{shared}")?;
348340
write!(f, "{order_quoting}")?;
349341
write!(f, "{http_client}")?;
@@ -382,7 +374,6 @@ impl std::fmt::Display for Arguments {
382374
f,
383375
"trusted_tokens_update_interval: {trusted_tokens_update_interval:?}"
384376
)?;
385-
display_list(f, "drivers", drivers.iter())?;
386377
writeln!(f, "submission_deadline: {submission_deadline}")?;
387378
display_option(f, "shadow", shadow)?;
388379
writeln!(f, "solve_deadline: {solve_deadline:?}")?;
@@ -432,75 +423,6 @@ impl std::fmt::Display for Arguments {
432423
}
433424
}
434425

435-
/// External solver driver configuration
436-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
437-
pub struct Solver {
438-
pub name: String,
439-
pub url: Url,
440-
pub submission_account: Account,
441-
pub fairness_threshold: Option<U256>,
442-
}
443-
444-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
445-
pub enum Account {
446-
/// AWS KMS is used to retrieve the solver public key
447-
Kms(Arn),
448-
/// Solver public key
449-
Address(Address),
450-
}
451-
452-
// Wrapper type for AWS ARN identifiers
453-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
454-
pub struct Arn(pub String);
455-
456-
impl FromStr for Arn {
457-
type Err = anyhow::Error;
458-
459-
fn from_str(s: &str) -> Result<Self, Self::Err> {
460-
// Could be more strict here, but this should suffice to catch unintended
461-
// configuration mistakes
462-
if s.starts_with("arn:aws:kms:") {
463-
Ok(Self(s.to_string()))
464-
} else {
465-
Err(anyhow!("Invalid ARN identifier: {}", s))
466-
}
467-
}
468-
}
469-
470-
impl Display for Solver {
471-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
472-
write!(f, "{}({})", self.name, self.url)
473-
}
474-
}
475-
476-
impl FromStr for Solver {
477-
type Err = anyhow::Error;
478-
479-
fn from_str(solver: &str) -> anyhow::Result<Self> {
480-
let parts: Vec<&str> = solver.split('|').collect();
481-
ensure!(parts.len() >= 3, "not enough arguments for external solver");
482-
let (name, url) = (parts[0], parts[1]);
483-
let url: Url = url.parse()?;
484-
let submission_account = match Arn::from_str(parts[2]) {
485-
Ok(value) => Account::Kms(value),
486-
_ => {
487-
Account::Address(Address::from_str(parts[2]).context("failed to parse submission")?)
488-
}
489-
};
490-
491-
let fairness_threshold = parts
492-
.get(3)
493-
.and_then(|value| U256::from_str_radix(value, 10).ok());
494-
495-
Ok(Self {
496-
name: name.to_owned(),
497-
url,
498-
fairness_threshold,
499-
submission_account,
500-
})
501-
}
502-
}
503-
504426
#[derive(clap::Parser, Debug, Clone)]
505427
pub struct FeePoliciesConfig {
506428
/// Describes how the protocol fees should be calculated.
@@ -693,7 +615,7 @@ impl FromStr for CowAmmConfig {
693615

694616
#[cfg(test)]
695617
mod test {
696-
use {super::*, alloy::primitives::address};
618+
use super::*;
697619

698620
#[test]
699621
fn test_fee_factor_limits() {
@@ -720,49 +642,4 @@ mod test {
720642
)
721643
}
722644
}
723-
724-
#[test]
725-
fn parse_driver_submission_account_address() {
726-
let argument = "name1|http://localhost:8080|0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
727-
let driver = Solver::from_str(argument).unwrap();
728-
let expected = Solver {
729-
name: "name1".into(),
730-
url: Url::parse("http://localhost:8080").unwrap(),
731-
fairness_threshold: None,
732-
submission_account: Account::Address(address!(
733-
"C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
734-
)),
735-
};
736-
assert_eq!(driver, expected);
737-
}
738-
739-
#[test]
740-
fn parse_driver_submission_account_arn() {
741-
let argument = "name1|http://localhost:8080|arn:aws:kms:supersecretstuff";
742-
let driver = Solver::from_str(argument).unwrap();
743-
let expected = Solver {
744-
name: "name1".into(),
745-
url: Url::parse("http://localhost:8080").unwrap(),
746-
fairness_threshold: None,
747-
submission_account: Account::Kms(
748-
Arn::from_str("arn:aws:kms:supersecretstuff").unwrap(),
749-
),
750-
};
751-
assert_eq!(driver, expected);
752-
}
753-
754-
#[test]
755-
fn parse_driver_with_threshold() {
756-
let argument = "name1|http://localhost:8080|0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2|1000000000000000000";
757-
let driver = Solver::from_str(argument).unwrap();
758-
let expected = Solver {
759-
name: "name1".into(),
760-
url: Url::parse("http://localhost:8080").unwrap(),
761-
submission_account: Account::Address(address!(
762-
"C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
763-
)),
764-
fairness_threshold: Some(U256::from(10).pow(U256::from(18))),
765-
};
766-
assert_eq!(driver, expected);
767-
}
768645
}

crates/autopilot/src/config/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use {
2+
crate::config::solver::Solver,
3+
anyhow::ensure,
4+
serde::{Deserialize, Serialize},
5+
std::path::Path,
6+
};
7+
8+
pub mod solver;
9+
10+
#[derive(Debug, Deserialize, Serialize)]
11+
pub struct Configuration {
12+
pub drivers: Vec<Solver>,
13+
}
14+
15+
impl Configuration {
16+
pub async fn from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
17+
Ok(toml::from_str(&tokio::fs::read_to_string(path).await?)?)
18+
}
19+
20+
pub async fn to_path<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
21+
Ok(tokio::fs::write(path, toml::to_string_pretty(self)?).await?)
22+
}
23+
24+
// Note for reviewers: if this and other validations are always applied,
25+
// we should instead move them to the deserialization stage
26+
// https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
27+
pub fn validate(self) -> anyhow::Result<Self> {
28+
ensure!(
29+
!self.drivers.is_empty(),
30+
"colocation is enabled but no drivers are configured"
31+
);
32+
Ok(self)
33+
}
34+
}
35+
36+
impl Default for Configuration {
37+
fn default() -> Self {
38+
Self {
39+
drivers: Default::default(),
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)