diff --git a/protocol-testing/Cargo.lock b/protocol-testing/Cargo.lock index b42e8a6..612168a 100644 --- a/protocol-testing/Cargo.lock +++ b/protocol-testing/Cargo.lock @@ -1238,6 +1238,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -1276,6 +1282,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dunce" version = "1.0.5" @@ -1425,6 +1437,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1461,6 +1482,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "funty" version = "2.0.0" @@ -2254,6 +2281,33 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -2271,6 +2325,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2609,6 +2669,36 @@ dependencies = [ "zerocopy 0.7.35", ] +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -2720,6 +2810,7 @@ name = "protocol-testing" version = "0.1.0" dependencies = [ "alloy", + "async-trait", "clap", "dotenv", "figment", @@ -3534,6 +3625,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "thiserror" version = "1.0.69" @@ -3931,8 +4028,7 @@ dependencies = [ [[package]] name = "tycho-client" -version = "0.56.5" -source = "git+https://github.com/propeller-heads/tycho-indexer.git?tag=0.56.5#2af8c1a5a61c5479eab5f7903b69943efa61e2c8" +version = "0.61.1" dependencies = [ "anyhow", "async-trait", @@ -3942,6 +4038,7 @@ dependencies = [ "hex", "hyper 0.14.32", "lru", + "mockall", "reqwest", "serde", "serde_json", @@ -3957,8 +4054,7 @@ dependencies = [ [[package]] name = "tycho-core" -version = "0.56.5" -source = "git+https://github.com/propeller-heads/tycho-indexer.git?tag=0.56.5#2af8c1a5a61c5479eab5f7903b69943efa61e2c8" +version = "0.61.1" dependencies = [ "anyhow", "async-trait", diff --git a/protocol-testing/Cargo.toml b/protocol-testing/Cargo.toml index 2b8ecf4..24f39c4 100644 --- a/protocol-testing/Cargo.toml +++ b/protocol-testing/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" # Logging & Tracing tracing = "0.1.37" # Tycho dependencies -tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.56.5" } -tycho-client = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-client", tag = "0.56.5" } +tycho-core = { path = "../../tycho-indexer/tycho-core", package = "tycho-core" } +tycho-client = { path = "../../tycho-indexer/tycho-client", package = "tycho-client" } # EVM dependencies alloy = { version = "0.5.4", features = ["arbitrary", "json", "dyn-abi", "sol-types", "contract", "provider-http"] } tokio = { version = "1", features = ["full"] } @@ -21,3 +21,4 @@ tracing-subscriber = "0.3.19" postgres = "0.19.10" serde_yaml = "0.9.34" dotenv = "0.15.0" +async-trait = "0.1.87" diff --git a/protocol-testing/src/main.rs b/protocol-testing/src/main.rs index a347328..89da115 100644 --- a/protocol-testing/src/main.rs +++ b/protocol-testing/src/main.rs @@ -1,8 +1,9 @@ mod config; mod rpc; mod test_runner; -mod utils; mod tycho_runner; +mod utils; +mod tycho_rpc; use clap::Parser; use tracing_subscriber::EnvFilter; diff --git a/protocol-testing/src/test_runner.rs b/protocol-testing/src/test_runner.rs index 5fb2853..acb2747 100644 --- a/protocol-testing/src/test_runner.rs +++ b/protocol-testing/src/test_runner.rs @@ -1,20 +1,19 @@ -use std::{ - path::{Path, PathBuf}, - thread::sleep, - time::Duration, -}; +use std::{collections::HashMap, path::PathBuf}; use figment::{ providers::{Format, Yaml}, Figment, }; use postgres::{Client, Error, NoTls}; -use tracing::{debug, field::debug, info}; +use tokio::runtime::Runtime; +use tracing::{debug, info}; +use tycho_core::dto::{Chain, ProtocolComponent}; use crate::{ config::{IntegrationTest, IntegrationTestsConfig}, + tycho_rpc::TychoClient, tycho_runner::TychoRunner, - utils::{build_spkg, modify_initial_block}, + utils::build_spkg, }; pub struct TestRunner { @@ -92,7 +91,7 @@ impl TestRunner { ) .expect("Failed to run Tycho"); - tycho_runner.run_with_rpc_server(validate_state) + tycho_runner.run_with_rpc_server(validate_state); } fn empty_database(&self) -> Result<(), Error> { @@ -113,7 +112,33 @@ impl TestRunner { } } -pub fn validate_state() { - // TODO: Implement +fn validate_state() { + let rt = Runtime::new().unwrap(); + + // Create Tycho client for the RPC server + let tycho_client = + TychoClient::new("http://localhost:4242").expect("Failed to create Tycho client"); + + let chain = Chain::Ethereum; + let protocol_system = "test_protocol"; + + let protocol_components = rt + .block_on(tycho_client.get_protocol_components(protocol_system, chain)) + .expect("Failed to get protocol components"); + + let protocol_states = rt + .block_on(tycho_client.get_protocol_state(protocol_system, chain)) + .expect("Failed to get protocol state"); + + // Create a map of component IDs to components for easy lookup + let components_by_id: HashMap = protocol_components + .into_iter() + .map(|c| (c.id.clone(), c)) + .collect(); + + info!("Found {} protocol components", components_by_id.len()); + info!("Found {} protocol states", protocol_states.len()); + + // TODO: Implement complete validation logic similar to Python code info!("Validating state..."); } diff --git a/protocol-testing/src/tycho_rpc.rs b/protocol-testing/src/tycho_rpc.rs new file mode 100644 index 0000000..ecd9db6 --- /dev/null +++ b/protocol-testing/src/tycho_rpc.rs @@ -0,0 +1,126 @@ +use std::{error::Error as StdError, fmt}; + +use tycho_client::{rpc::RPCClient, HttpRPCClient}; +use tycho_core::{ + dto::{ + Chain, ProtocolComponent, ProtocolComponentsRequestBody, ResponseAccount, + ResponseProtocolState, VersionParam, + }, + models::Address, +}; + +/// Custom error type for RPC operations +#[derive(Debug)] +pub enum RpcError { + ClientError(String), + ResponseError(String), +} + +impl fmt::Display for RpcError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RpcError::ClientError(msg) => write!(f, "RPC client error: {}", msg), + RpcError::ResponseError(msg) => write!(f, "RPC response error: {}", msg), + } + } +} + +impl StdError for RpcError {} + +impl From> for RpcError { + fn from(error: Box) -> Self { + RpcError::ClientError(error.to_string()) + } +} + +impl From for RpcError { + fn from(error: tycho_client::RPCError) -> Self { + RpcError::ClientError(error.to_string()) + } +} + +/// Client for interacting with the Tycho RPC server +pub struct TychoClient { + http_client: HttpRPCClient, +} + +impl TychoClient { + pub fn new(host: &str) -> Result { + let http_client = + HttpRPCClient::new(host, None).map_err(|e| RpcError::ClientError(e.to_string()))?; + Ok(Self { http_client }) + } + + /// Gets protocol components from the RPC server + pub async fn get_protocol_components( + &self, + protocol_system: &str, + chain: Chain, + ) -> Result, RpcError> { + let request = ProtocolComponentsRequestBody::system_filtered(protocol_system, None, chain); + + let chunk_size = 100; + let concurrency = 1; + + let response = self + .http_client + .get_protocol_components_paginated(&request, chunk_size, concurrency) + .await?; + + Ok(response.protocol_components) + } + + /// Gets protocol state from the RPC server + pub async fn get_protocol_state( + &self, + protocol_system: &str, + chain: Chain, + ) -> Result, RpcError> { + let chunk_size = 100; + let concurrency = 1; + let ids: &[String] = &[]; + let version: tycho_core::dto::VersionParam = VersionParam::default(); + + let protocol_states = self + .http_client + .get_protocol_states_paginated( + chain, + ids, + protocol_system, + true, + &version, + chunk_size, + concurrency, + ) + .await?; + + Ok(protocol_states.states) + } + + /// Gets contract state from the RPC server + pub async fn get_contract_state( + &self, + contract_ids: Vec
, + protocol_system: &str, + chain: Chain, + ) -> Result, RpcError> { + // Pagination parameters + let chunk_size = 100; + let concurrency = 1; + let version: tycho_core::dto::VersionParam = VersionParam::default(); + + let contract_states = self + .http_client + .get_contract_state_paginated( + chain, + &contract_ids, + protocol_system, + &version, + chunk_size, + concurrency, + ) + .await?; + + Ok(contract_states.accounts) + } +} diff --git a/protocol-testing/src/tycho_runner.rs b/protocol-testing/src/tycho_runner.rs index 5ea8f0a..aaa6e8d 100644 --- a/protocol-testing/src/tycho_runner.rs +++ b/protocol-testing/src/tycho_runner.rs @@ -1,12 +1,7 @@ use std::{ - env, io::{BufRead, BufReader}, - path::Path, process::{Child, Command, Stdio}, - sync::{ - mpsc::{self, Receiver, Sender}, - Arc, Mutex, - }, + sync::mpsc::{self, Receiver, Sender}, thread, time::Duration, }; @@ -110,7 +105,7 @@ impl TychoRunner { let binary_path = "tycho-indexer"; let mut cmd = Command::new(binary_path) - .args(&["--database-url", db_url.as_str(), "rpc"]) + .args(["--database-url", db_url.as_str(), "rpc"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("RUST_LOG", "info")