|
|
@@ -1,6 +1,7 @@
|
|
|
use {
|
|
|
crate::{
|
|
|
chain::reader::{BlockNumber, BlockStatus, EntropyReader},
|
|
|
+ config::Config,
|
|
|
history::History,
|
|
|
state::MonitoredHashChainState,
|
|
|
},
|
|
|
@@ -76,6 +77,9 @@ pub struct ApiState {
|
|
|
pub metrics: Arc<ApiMetrics>,
|
|
|
|
|
|
pub explorer_metrics: Arc<ExplorerMetrics>,
|
|
|
+
|
|
|
+ /// Parsed configuration
|
|
|
+ pub config: Config,
|
|
|
}
|
|
|
|
|
|
impl ApiState {
|
|
|
@@ -83,6 +87,7 @@ impl ApiState {
|
|
|
chains: Arc<RwLock<HashMap<ChainId, ApiBlockChainState>>>,
|
|
|
metrics_registry: Arc<RwLock<Registry>>,
|
|
|
history: Arc<History>,
|
|
|
+ config: Config,
|
|
|
) -> ApiState {
|
|
|
let metrics = ApiMetrics {
|
|
|
http_requests: Family::default(),
|
|
|
@@ -103,6 +108,7 @@ impl ApiState {
|
|
|
explorer_metrics,
|
|
|
history,
|
|
|
metrics_registry,
|
|
|
+ config,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -234,9 +240,10 @@ mod test {
|
|
|
crate::{
|
|
|
api::{
|
|
|
self, ApiBlockChainState, ApiState, BinaryEncoding, Blob, BlockchainState,
|
|
|
- GetRandomValueResponse,
|
|
|
+ ChainConfigSummary, GetRandomValueResponse,
|
|
|
},
|
|
|
chain::reader::{mock::MockEntropyReader, BlockStatus},
|
|
|
+ config::Config,
|
|
|
history::History,
|
|
|
state::{HashChainState, MonitoredHashChainState, PebbleHashChain},
|
|
|
},
|
|
|
@@ -315,10 +322,40 @@ mod test {
|
|
|
ApiBlockChainState::Initialized(avax_state),
|
|
|
);
|
|
|
|
|
|
+ // Create a minimal config for testing
|
|
|
+ let config = Config {
|
|
|
+ chains: HashMap::new(),
|
|
|
+ provider: crate::config::ProviderConfig {
|
|
|
+ uri: "http://localhost:8080/".to_string(),
|
|
|
+ address: PROVIDER,
|
|
|
+ private_key: crate::config::SecretString {
|
|
|
+ value: Some("0xabcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ secret: crate::config::SecretString {
|
|
|
+ value: Some("abcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ chain_length: 100000,
|
|
|
+ chain_sample_interval: 10,
|
|
|
+ fee_manager: None,
|
|
|
+ },
|
|
|
+ keeper: crate::config::KeeperConfig {
|
|
|
+ private_key: crate::config::SecretString {
|
|
|
+ value: Some("0xabcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ fee_manager_private_key: None,
|
|
|
+ other_keeper_addresses: vec![],
|
|
|
+ replica_config: None,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
let api_state = ApiState::new(
|
|
|
Arc::new(RwLock::new(chains)),
|
|
|
metrics_registry,
|
|
|
Arc::new(History::new().await.unwrap()),
|
|
|
+ config,
|
|
|
)
|
|
|
.await;
|
|
|
|
|
|
@@ -538,4 +575,183 @@ mod test {
|
|
|
)
|
|
|
.await;
|
|
|
}
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn test_chain_configs() {
|
|
|
+ let (server, _, _) = test_server().await;
|
|
|
+
|
|
|
+ // Test the chain configs endpoint
|
|
|
+ let response = server.get("/v1/chains/configs").await;
|
|
|
+ response.assert_status(StatusCode::OK);
|
|
|
+
|
|
|
+ // Parse the response as JSON
|
|
|
+ let configs: Vec<ChainConfigSummary> = response.json();
|
|
|
+
|
|
|
+ // Verify the response structure - should be empty for test server
|
|
|
+ assert_eq!(configs.len(), 0, "Should return empty configs for test server");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn test_chain_configs_with_data() {
|
|
|
+ // Create a test server with actual chain configurations
|
|
|
+ let eth_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
|
|
|
+ let avax_read = Arc::new(MockEntropyReader::with_requests(10, &[]));
|
|
|
+
|
|
|
+ let eth_state = MonitoredHashChainState::new(
|
|
|
+ ETH_CHAIN.clone(),
|
|
|
+ Default::default(),
|
|
|
+ "ethereum".into(),
|
|
|
+ PROVIDER,
|
|
|
+ );
|
|
|
+
|
|
|
+ let eth_state = BlockchainState {
|
|
|
+ id: "ethereum".into(),
|
|
|
+ network_id: 1,
|
|
|
+ state: Arc::new(eth_state),
|
|
|
+ contract: eth_read.clone(),
|
|
|
+ provider_address: PROVIDER,
|
|
|
+ reveal_delay_blocks: 1,
|
|
|
+ confirmed_block_status: BlockStatus::Latest,
|
|
|
+ };
|
|
|
+
|
|
|
+ let avax_state = MonitoredHashChainState::new(
|
|
|
+ AVAX_CHAIN.clone(),
|
|
|
+ Default::default(),
|
|
|
+ "avalanche".into(),
|
|
|
+ PROVIDER,
|
|
|
+ );
|
|
|
+
|
|
|
+ let avax_state = BlockchainState {
|
|
|
+ id: "avalanche".into(),
|
|
|
+ network_id: 43114,
|
|
|
+ state: Arc::new(avax_state),
|
|
|
+ contract: avax_read.clone(),
|
|
|
+ provider_address: PROVIDER,
|
|
|
+ reveal_delay_blocks: 2,
|
|
|
+ confirmed_block_status: BlockStatus::Latest,
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut chains = HashMap::new();
|
|
|
+ chains.insert(
|
|
|
+ "ethereum".into(),
|
|
|
+ ApiBlockChainState::Initialized(eth_state),
|
|
|
+ );
|
|
|
+ chains.insert(
|
|
|
+ "avalanche".into(),
|
|
|
+ ApiBlockChainState::Initialized(avax_state),
|
|
|
+ );
|
|
|
+
|
|
|
+ // Create a config with actual chain data
|
|
|
+ let mut config_chains = HashMap::new();
|
|
|
+ config_chains.insert(
|
|
|
+ "ethereum".to_string(),
|
|
|
+ crate::config::EthereumConfig {
|
|
|
+ geth_rpc_addr: "http://localhost:8545".to_string(),
|
|
|
+ contract_addr: Address::from_low_u64_be(0x1234),
|
|
|
+ reveal_delay_blocks: 1,
|
|
|
+ confirmed_block_status: BlockStatus::Latest,
|
|
|
+ backlog_range: 1000,
|
|
|
+ legacy_tx: false,
|
|
|
+ gas_limit: 500000,
|
|
|
+ priority_fee_multiplier_pct: 100,
|
|
|
+ escalation_policy: crate::config::EscalationPolicyConfig::default(),
|
|
|
+ min_profit_pct: 0,
|
|
|
+ target_profit_pct: 20,
|
|
|
+ max_profit_pct: 100,
|
|
|
+ min_keeper_balance: 100000000000000000,
|
|
|
+ fee: 1500000000000000,
|
|
|
+ sync_fee_only_on_register: true,
|
|
|
+ commitments: None,
|
|
|
+ max_num_hashes: None,
|
|
|
+ block_delays: vec![5],
|
|
|
+ },
|
|
|
+ );
|
|
|
+ config_chains.insert(
|
|
|
+ "avalanche".to_string(),
|
|
|
+ crate::config::EthereumConfig {
|
|
|
+ geth_rpc_addr: "http://localhost:9650".to_string(),
|
|
|
+ contract_addr: Address::from_low_u64_be(0x5678),
|
|
|
+ reveal_delay_blocks: 2,
|
|
|
+ confirmed_block_status: BlockStatus::Latest,
|
|
|
+ backlog_range: 1000,
|
|
|
+ legacy_tx: false,
|
|
|
+ gas_limit: 600000,
|
|
|
+ priority_fee_multiplier_pct: 100,
|
|
|
+ escalation_policy: crate::config::EscalationPolicyConfig::default(),
|
|
|
+ min_profit_pct: 0,
|
|
|
+ target_profit_pct: 20,
|
|
|
+ max_profit_pct: 100,
|
|
|
+ min_keeper_balance: 100000000000000000,
|
|
|
+ fee: 2000000000000000,
|
|
|
+ sync_fee_only_on_register: true,
|
|
|
+ commitments: None,
|
|
|
+ max_num_hashes: None,
|
|
|
+ block_delays: vec![5],
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ let config = Config {
|
|
|
+ chains: config_chains,
|
|
|
+ provider: crate::config::ProviderConfig {
|
|
|
+ uri: "http://localhost:8080/".to_string(),
|
|
|
+ address: PROVIDER,
|
|
|
+ private_key: crate::config::SecretString {
|
|
|
+ value: Some("0xabcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ secret: crate::config::SecretString {
|
|
|
+ value: Some("abcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ chain_length: 100000,
|
|
|
+ chain_sample_interval: 10,
|
|
|
+ fee_manager: None,
|
|
|
+ },
|
|
|
+ keeper: crate::config::KeeperConfig {
|
|
|
+ private_key: crate::config::SecretString {
|
|
|
+ value: Some("0xabcd".to_string()),
|
|
|
+ file: None,
|
|
|
+ },
|
|
|
+ fee_manager_private_key: None,
|
|
|
+ other_keeper_addresses: vec![],
|
|
|
+ replica_config: None,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ let metrics_registry = Arc::new(RwLock::new(Registry::default()));
|
|
|
+ let api_state = ApiState::new(
|
|
|
+ Arc::new(RwLock::new(chains)),
|
|
|
+ metrics_registry,
|
|
|
+ Arc::new(History::new().await.unwrap()),
|
|
|
+ config,
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+
|
|
|
+ let app = api::routes(api_state);
|
|
|
+ let server = TestServer::new(app).unwrap();
|
|
|
+
|
|
|
+ // Test the chain configs endpoint
|
|
|
+ let response = server.get("/v1/chains/configs").await;
|
|
|
+ response.assert_status(StatusCode::OK);
|
|
|
+
|
|
|
+ // Parse the response as JSON
|
|
|
+ let configs: Vec<ChainConfigSummary> = response.json();
|
|
|
+
|
|
|
+ // Verify we have 2 chains
|
|
|
+ assert_eq!(configs.len(), 2, "Should return 2 chain configs");
|
|
|
+
|
|
|
+ // Find ethereum config
|
|
|
+ let eth_config = configs.iter().find(|c| c.name == "ethereum").expect("Ethereum config not found");
|
|
|
+ assert_eq!(eth_config.contract_addr, "0x0000000000000000000000000000000000001234");
|
|
|
+ assert_eq!(eth_config.reveal_delay_blocks, 1);
|
|
|
+ assert_eq!(eth_config.gas_limit, 500000);
|
|
|
+ assert_eq!(eth_config.fee, 1500000000000000);
|
|
|
+
|
|
|
+ // Find avalanche config
|
|
|
+ let avax_config = configs.iter().find(|c| c.name == "avalanche").expect("Avalanche config not found");
|
|
|
+ assert_eq!(avax_config.contract_addr, "0x0000000000000000000000000000000000005678");
|
|
|
+ assert_eq!(avax_config.reveal_delay_blocks, 2);
|
|
|
+ assert_eq!(avax_config.gas_limit, 600000);
|
|
|
+ assert_eq!(avax_config.fee, 2000000000000000);
|
|
|
+ }
|
|
|
}
|