Skip to content

Commit 5b08ea6

Browse files
committed
requested comments
1 parent 0eae35a commit 5b08ea6

File tree

4 files changed

+232
-22
lines changed

4 files changed

+232
-22
lines changed

apps/fortuna/src/api.rs

Lines changed: 217 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use {
22
crate::{
33
chain::reader::{BlockNumber, BlockStatus, EntropyReader},
4+
config::Config,
45
history::History,
56
state::MonitoredHashChainState,
67
},
@@ -76,13 +77,17 @@ pub struct ApiState {
7677
pub metrics: Arc<ApiMetrics>,
7778

7879
pub explorer_metrics: Arc<ExplorerMetrics>,
80+
81+
/// Parsed configuration
82+
pub config: Config,
7983
}
8084

8185
impl ApiState {
8286
pub async fn new(
8387
chains: Arc<RwLock<HashMap<ChainId, ApiBlockChainState>>>,
8488
metrics_registry: Arc<RwLock<Registry>>,
8589
history: Arc<History>,
90+
config: Config,
8691
) -> ApiState {
8792
let metrics = ApiMetrics {
8893
http_requests: Family::default(),
@@ -103,6 +108,7 @@ impl ApiState {
103108
explorer_metrics,
104109
history,
105110
metrics_registry,
111+
config,
106112
}
107113
}
108114
}
@@ -234,9 +240,10 @@ mod test {
234240
crate::{
235241
api::{
236242
self, ApiBlockChainState, ApiState, BinaryEncoding, Blob, BlockchainState,
237-
GetRandomValueResponse,
243+
ChainConfigSummary, GetRandomValueResponse,
238244
},
239245
chain::reader::{mock::MockEntropyReader, BlockStatus},
246+
config::Config,
240247
history::History,
241248
state::{HashChainState, MonitoredHashChainState, PebbleHashChain},
242249
},
@@ -315,10 +322,40 @@ mod test {
315322
ApiBlockChainState::Initialized(avax_state),
316323
);
317324

325+
// Create a minimal config for testing
326+
let config = Config {
327+
chains: HashMap::new(),
328+
provider: crate::config::ProviderConfig {
329+
uri: "http://localhost:8080/".to_string(),
330+
address: PROVIDER,
331+
private_key: crate::config::SecretString {
332+
value: Some("0xabcd".to_string()),
333+
file: None,
334+
},
335+
secret: crate::config::SecretString {
336+
value: Some("abcd".to_string()),
337+
file: None,
338+
},
339+
chain_length: 100000,
340+
chain_sample_interval: 10,
341+
fee_manager: None,
342+
},
343+
keeper: crate::config::KeeperConfig {
344+
private_key: crate::config::SecretString {
345+
value: Some("0xabcd".to_string()),
346+
file: None,
347+
},
348+
fee_manager_private_key: None,
349+
other_keeper_addresses: vec![],
350+
replica_config: None,
351+
},
352+
};
353+
318354
let api_state = ApiState::new(
319355
Arc::new(RwLock::new(chains)),
320356
metrics_registry,
321357
Arc::new(History::new().await.unwrap()),
358+
config,
322359
)
323360
.await;
324361

@@ -538,4 +575,183 @@ mod test {
538575
)
539576
.await;
540577
}
578+
579+
#[tokio::test]
580+
async fn test_chain_configs() {
581+
let (server, _, _) = test_server().await;
582+
583+
// Test the chain configs endpoint
584+
let response = server.get("/v1/chains/configs").await;
585+
response.assert_status(StatusCode::OK);
586+
587+
// Parse the response as JSON
588+
let configs: Vec<ChainConfigSummary> = response.json();
589+
590+
// Verify the response structure - should be empty for test server
591+
assert_eq!(configs.len(), 0, "Should return empty configs for test server");
592+
}
593+
594+
#[tokio::test]
595+
async fn test_chain_configs_with_data() {
596+
// Create a test server with actual chain configurations
597+
let eth_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
598+
let avax_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
599+
600+
let eth_state = MonitoredHashChainState::new(
601+
ETH_CHAIN.clone(),
602+
Default::default(),
603+
"ethereum".into(),
604+
PROVIDER,
605+
);
606+
607+
let eth_state = BlockchainState {
608+
id: "ethereum".into(),
609+
network_id: 1,
610+
state: Arc::new(eth_state),
611+
contract: eth_read.clone(),
612+
provider_address: PROVIDER,
613+
reveal_delay_blocks: 1,
614+
confirmed_block_status: BlockStatus::Latest,
615+
};
616+
617+
let avax_state = MonitoredHashChainState::new(
618+
AVAX_CHAIN.clone(),
619+
Default::default(),
620+
"avalanche".into(),
621+
PROVIDER,
622+
);
623+
624+
let avax_state = BlockchainState {
625+
id: "avalanche".into(),
626+
network_id: 43114,
627+
state: Arc::new(avax_state),
628+
contract: avax_read.clone(),
629+
provider_address: PROVIDER,
630+
reveal_delay_blocks: 2,
631+
confirmed_block_status: BlockStatus::Latest,
632+
};
633+
634+
let mut chains = HashMap::new();
635+
chains.insert(
636+
"ethereum".into(),
637+
ApiBlockChainState::Initialized(eth_state),
638+
);
639+
chains.insert(
640+
"avalanche".into(),
641+
ApiBlockChainState::Initialized(avax_state),
642+
);
643+
644+
// Create a config with actual chain data
645+
let mut config_chains = HashMap::new();
646+
config_chains.insert(
647+
"ethereum".to_string(),
648+
crate::config::EthereumConfig {
649+
geth_rpc_addr: "http://localhost:8545".to_string(),
650+
contract_addr: Address::from_low_u64_be(0x1234),
651+
reveal_delay_blocks: 1,
652+
confirmed_block_status: BlockStatus::Latest,
653+
backlog_range: 1000,
654+
legacy_tx: false,
655+
gas_limit: 500000,
656+
priority_fee_multiplier_pct: 100,
657+
escalation_policy: crate::config::EscalationPolicyConfig::default(),
658+
min_profit_pct: 0,
659+
target_profit_pct: 20,
660+
max_profit_pct: 100,
661+
min_keeper_balance: 100000000000000000,
662+
fee: 1500000000000000,
663+
sync_fee_only_on_register: true,
664+
commitments: None,
665+
max_num_hashes: None,
666+
block_delays: vec![5],
667+
},
668+
);
669+
config_chains.insert(
670+
"avalanche".to_string(),
671+
crate::config::EthereumConfig {
672+
geth_rpc_addr: "http://localhost:9650".to_string(),
673+
contract_addr: Address::from_low_u64_be(0x5678),
674+
reveal_delay_blocks: 2,
675+
confirmed_block_status: BlockStatus::Latest,
676+
backlog_range: 1000,
677+
legacy_tx: false,
678+
gas_limit: 600000,
679+
priority_fee_multiplier_pct: 100,
680+
escalation_policy: crate::config::EscalationPolicyConfig::default(),
681+
min_profit_pct: 0,
682+
target_profit_pct: 20,
683+
max_profit_pct: 100,
684+
min_keeper_balance: 100000000000000000,
685+
fee: 2000000000000000,
686+
sync_fee_only_on_register: true,
687+
commitments: None,
688+
max_num_hashes: None,
689+
block_delays: vec![5],
690+
},
691+
);
692+
693+
let config = Config {
694+
chains: config_chains,
695+
provider: crate::config::ProviderConfig {
696+
uri: "http://localhost:8080/".to_string(),
697+
address: PROVIDER,
698+
private_key: crate::config::SecretString {
699+
value: Some("0xabcd".to_string()),
700+
file: None,
701+
},
702+
secret: crate::config::SecretString {
703+
value: Some("abcd".to_string()),
704+
file: None,
705+
},
706+
chain_length: 100000,
707+
chain_sample_interval: 10,
708+
fee_manager: None,
709+
},
710+
keeper: crate::config::KeeperConfig {
711+
private_key: crate::config::SecretString {
712+
value: Some("0xabcd".to_string()),
713+
file: None,
714+
},
715+
fee_manager_private_key: None,
716+
other_keeper_addresses: vec![],
717+
replica_config: None,
718+
},
719+
};
720+
721+
let metrics_registry = Arc::new(RwLock::new(Registry::default()));
722+
let api_state = ApiState::new(
723+
Arc::new(RwLock::new(chains)),
724+
metrics_registry,
725+
Arc::new(History::new().await.unwrap()),
726+
config,
727+
)
728+
.await;
729+
730+
let app = api::routes(api_state);
731+
let server = TestServer::new(app).unwrap();
732+
733+
// Test the chain configs endpoint
734+
let response = server.get("/v1/chains/configs").await;
735+
response.assert_status(StatusCode::OK);
736+
737+
// Parse the response as JSON
738+
let configs: Vec<ChainConfigSummary> = response.json();
739+
740+
// Verify we have 2 chains
741+
assert_eq!(configs.len(), 2, "Should return 2 chain configs");
742+
743+
// Find ethereum config
744+
let eth_config = configs.iter().find(|c| c.name == "ethereum").expect("Ethereum config not found");
745+
assert_eq!(eth_config.contract_addr, "0x0000000000000000000000000000000000001234");
746+
assert_eq!(eth_config.reveal_delay_blocks, 1);
747+
assert_eq!(eth_config.gas_limit, 500000);
748+
assert_eq!(eth_config.fee, 1500000000000000);
749+
750+
// Find avalanche config
751+
let avax_config = configs.iter().find(|c| c.name == "avalanche").expect("Avalanche config not found");
752+
assert_eq!(avax_config.contract_addr, "0x0000000000000000000000000000000000005678");
753+
assert_eq!(avax_config.reveal_delay_blocks, 2);
754+
assert_eq!(avax_config.gas_limit, 600000);
755+
assert_eq!(avax_config.fee, 2000000000000000);
756+
}
541757
}

apps/fortuna/src/api/config.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::{
2-
api::{ApiState, RestError},
3-
config::Config,
1+
use {
2+
crate::api::{ApiState, RestError},
3+
axum::{extract::State, Json},
4+
serde::Serialize,
45
};
5-
use axum::{extract::State, Json};
66

7-
#[derive(serde::Serialize)]
7+
#[derive(Serialize, serde::Deserialize)]
88
pub struct ChainConfigSummary {
99
pub name: String,
1010
pub contract_addr: String,
@@ -13,19 +13,9 @@ pub struct ChainConfigSummary {
1313
pub fee: u128,
1414
}
1515

16-
pub async fn get_chain_configs(
17-
State(_state): State<ApiState>,
18-
) -> anyhow::Result<Json<Vec<ChainConfigSummary>>, RestError> {
19-
let yaml_content = std::fs::read_to_string("config.yaml").map_err(|e| {
20-
tracing::error!("Failed to read config file: {}", e);
21-
RestError::Unknown
22-
})?;
23-
let config: Config = serde_yaml::from_str(&yaml_content).map_err(|e| {
24-
tracing::error!("Failed to parse config file: {}", e);
25-
RestError::Unknown
26-
})?;
16+
pub async fn get_chain_configs(State(state): State<ApiState>) -> Result<Json<Vec<ChainConfigSummary>>, RestError> {
2717
let mut configs = Vec::new();
28-
for (name, chain) in config.chains.iter() {
18+
for (name, chain) in state.config.chains.iter() {
2919
configs.push(ChainConfigSummary {
3020
name: name.clone(),
3121
contract_addr: format!("0x{:x}", chain.contract_addr),

apps/fortuna/src/command/run.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub async fn run_api(
2828
chains: Arc<RwLock<HashMap<String, ApiBlockChainState>>>,
2929
metrics_registry: Arc<RwLock<Registry>>,
3030
history: Arc<History>,
31+
config: Config,
3132
mut rx_exit: watch::Receiver<bool>,
3233
) -> Result<()> {
3334
#[derive(OpenApi)]
@@ -54,7 +55,7 @@ pub async fn run_api(
5455
)]
5556
struct ApiDoc;
5657

57-
let api_state = api::ApiState::new(chains, metrics_registry, history).await;
58+
let api_state = api::ApiState::new(chains, metrics_registry, history, config).await;
5859

5960
// Initialize Axum Router. Note the type here is a `Router<State>` due to the use of the
6061
// `with_state` method which replaces `Body` with `State` in the type signature.
@@ -170,6 +171,7 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
170171
chains.clone(),
171172
metrics_registry.clone(),
172173
history,
174+
config,
173175
rx_exit,
174176
)
175177
.await?;

target_chains/cosmwasm/examples/cw-contract/Cargo.lock

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)