feat(Testing SDK): Pass adapter path to decoder (#249)
* feat: Pass adapter contract to decoder This was the most elegant solution we could come up with at the moment for having the proper adapter in the builder. Notes: - The default adapter bytecode in tycho-simulation is still loaded at compile time. - If the adapter bytecode is passed to the decoder, it will be loaded dynamically at runtime and used instead the bytecodes in tycho-simulation. - The adapter bytecode is used in the builder to get capabilities and thus spot prices before returning the state, so just overwriting the adapter in the state is way too cumbersome. We went with this solution since it was the lesser evil, even though we know it leaks VM-specific info to non-vm protocols (which was already being done anyway with the balances). * feat: Take a struct of vm_attributes instead of just adapter path So that we don't need to change the interface in the future * chore: remove comment - we are no longer blocked This is fixed with the latest change to pass adapter contract to decoder * feat: Use DecoderContext instead of VMAttributes - More easily extendable - Doesn't break the existing decoder interface --------- Co-authored-by: TAMARA LIPOWSKI <data.lipowski@extaccount.com>
This commit is contained in:
4
protocol-testing/Cargo.lock
generated
4
protocol-testing/Cargo.lock
generated
@@ -7810,8 +7810,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tycho-simulation"
|
name = "tycho-simulation"
|
||||||
version = "0.155.2"
|
version = "0.156.0"
|
||||||
source = "git+https://github.com/propeller-heads/tycho-simulation.git?tag=0.155.2#b1f5a6e2b7c70a2871a1dd9e19f53d48421be5c8"
|
source = "git+https://github.com/propeller-heads/tycho-simulation.git?tag=0.156.0#1983a787440e8ae757626d808a6e619baffc52f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ tracing = "0.1.37"
|
|||||||
# Tycho dependencies
|
# Tycho dependencies
|
||||||
tycho-common = "0.82.0"
|
tycho-common = "0.82.0"
|
||||||
tycho-client = "0.82.0"
|
tycho-client = "0.82.0"
|
||||||
tycho-simulation = { git = "https://github.com/propeller-heads/tycho-simulation.git", tag = "0.155.2", features = ["evm"] }
|
tycho-simulation = { git = "https://github.com/propeller-heads/tycho-simulation.git", tag = "0.156.0", features = ["evm"] }
|
||||||
## TODO: for local development
|
## TODO: for local development
|
||||||
#tycho-simulation = { path = "../../tycho-simulation" }
|
#tycho-simulation = { path = "../../tycho-simulation" }
|
||||||
# EVM dependencies
|
# EVM dependencies
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ impl ProtocolComponentExpectation {
|
|||||||
match other_value {
|
match other_value {
|
||||||
Some(other_value) => {
|
Some(other_value) => {
|
||||||
if value != other_value {
|
if value != other_value {
|
||||||
let self_value = format!("{:?}", value);
|
let self_value = format!("{value:?}");
|
||||||
let other_value = format!("{:?}", other_value);
|
let other_value = format!("{other_value:?}");
|
||||||
let diff = self.format_diff(
|
let diff = self.format_diff(
|
||||||
"static_attributes",
|
"static_attributes",
|
||||||
&self_value,
|
&self_value,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use tycho_simulation::{
|
|||||||
engine_db::tycho_db::PreCachedDB,
|
engine_db::tycho_db::PreCachedDB,
|
||||||
protocol::{u256_num::bytes_to_u256, vm::state::EVMPoolState},
|
protocol::{u256_num::bytes_to_u256, vm::state::EVMPoolState},
|
||||||
},
|
},
|
||||||
|
protocol::models::DecoderContext,
|
||||||
tycho_client::feed::{
|
tycho_client::feed::{
|
||||||
synchronizer::{ComponentWithState, Snapshot, StateSyncMessage},
|
synchronizer::{ComponentWithState, Snapshot, StateSyncMessage},
|
||||||
FeedMessage,
|
FeedMessage,
|
||||||
@@ -28,6 +29,7 @@ use tycho_simulation::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
adapter_builder::AdapterContractBuilder,
|
||||||
config::{IntegrationTest, IntegrationTestsConfig, ProtocolComponentWithTestConfig},
|
config::{IntegrationTest, IntegrationTestsConfig, ProtocolComponentWithTestConfig},
|
||||||
rpc::RPCProvider,
|
rpc::RPCProvider,
|
||||||
tycho_rpc::TychoClient,
|
tycho_rpc::TychoClient,
|
||||||
@@ -40,12 +42,17 @@ pub struct TestRunner {
|
|||||||
db_url: String,
|
db_url: String,
|
||||||
vm_traces: bool,
|
vm_traces: bool,
|
||||||
substreams_path: PathBuf,
|
substreams_path: PathBuf,
|
||||||
|
adapter_contract_builder: AdapterContractBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestRunner {
|
impl TestRunner {
|
||||||
pub fn new(package: String, tycho_logs: bool, db_url: String, vm_traces: bool) -> Self {
|
pub fn new(package: String, tycho_logs: bool, db_url: String, vm_traces: bool) -> Self {
|
||||||
let substreams_path = PathBuf::from("../substreams").join(&package);
|
let substreams_path = PathBuf::from("../substreams").join(&package);
|
||||||
Self { tycho_logs, db_url, vm_traces, substreams_path }
|
let repo_root = env::current_dir().expect("Failed to get current directory");
|
||||||
|
let evm_path = repo_root.join("../evm");
|
||||||
|
let adapter_contract_builder =
|
||||||
|
AdapterContractBuilder::new(evm_path.to_string_lossy().to_string());
|
||||||
|
Self { tycho_logs, db_url, vm_traces, substreams_path, adapter_contract_builder }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_tests(&self) -> miette::Result<()> {
|
pub fn run_tests(&self) -> miette::Result<()> {
|
||||||
@@ -96,7 +103,7 @@ impl TestRunner {
|
|||||||
|
|
||||||
fn parse_config(config_yaml_path: &PathBuf) -> miette::Result<IntegrationTestsConfig> {
|
fn parse_config(config_yaml_path: &PathBuf) -> miette::Result<IntegrationTestsConfig> {
|
||||||
info!("Config YAML: {}", config_yaml_path.display());
|
info!("Config YAML: {}", config_yaml_path.display());
|
||||||
let yaml = Yaml::file(&config_yaml_path);
|
let yaml = Yaml::file(config_yaml_path);
|
||||||
let figment = Figment::new().merge(yaml);
|
let figment = Figment::new().merge(yaml);
|
||||||
let config = figment
|
let config = figment
|
||||||
.extract::<IntegrationTestsConfig>()
|
.extract::<IntegrationTestsConfig>()
|
||||||
@@ -145,7 +152,16 @@ impl TestRunner {
|
|||||||
.wrap_err("Failed to run Tycho")?;
|
.wrap_err("Failed to run Tycho")?;
|
||||||
|
|
||||||
let _ = tycho_runner.run_with_rpc_server(
|
let _ = tycho_runner.run_with_rpc_server(
|
||||||
validate_state,
|
|expected_components, start_block, stop_block, skip_balance_check| {
|
||||||
|
validate_state(
|
||||||
|
expected_components,
|
||||||
|
start_block,
|
||||||
|
stop_block,
|
||||||
|
skip_balance_check,
|
||||||
|
config,
|
||||||
|
&self.adapter_contract_builder,
|
||||||
|
)
|
||||||
|
},
|
||||||
&test.expected_components,
|
&test.expected_components,
|
||||||
test.start_block,
|
test.start_block,
|
||||||
test.stop_block,
|
test.stop_block,
|
||||||
@@ -176,6 +192,8 @@ fn validate_state(
|
|||||||
start_block: u64,
|
start_block: u64,
|
||||||
stop_block: u64,
|
stop_block: u64,
|
||||||
skip_balance_check: bool,
|
skip_balance_check: bool,
|
||||||
|
config: &IntegrationTestsConfig,
|
||||||
|
adapter_contract_builder: &AdapterContractBuilder,
|
||||||
) -> miette::Result<()> {
|
) -> miette::Result<()> {
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
@@ -263,8 +281,42 @@ fn validate_state(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Run Tycho Simulation
|
// Step 3: Run Tycho Simulation
|
||||||
|
|
||||||
|
// Build/find the adapter contract
|
||||||
|
let adapter_contract_path =
|
||||||
|
match adapter_contract_builder.find_contract(&config.adapter_contract) {
|
||||||
|
Ok(path) => {
|
||||||
|
info!("Found adapter contract at: {}", path.display());
|
||||||
|
path
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
info!("Adapter contract not found, building it...");
|
||||||
|
adapter_contract_builder
|
||||||
|
.build_target(
|
||||||
|
&config.adapter_contract,
|
||||||
|
config
|
||||||
|
.adapter_build_signature
|
||||||
|
.as_deref(),
|
||||||
|
config.adapter_build_args.as_deref(),
|
||||||
|
)
|
||||||
|
.wrap_err("Failed to build adapter contract")?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("Using adapter contract: {}", adapter_contract_path.display());
|
||||||
|
let adapter_contract_path_str: &str = adapter_contract_path.to_str().unwrap();
|
||||||
|
|
||||||
let mut decoder = TychoStreamDecoder::new();
|
let mut decoder = TychoStreamDecoder::new();
|
||||||
decoder.register_decoder::<EVMPoolState<PreCachedDB>>("test_protocol");
|
let decoder_context = DecoderContext::new().vm_adapter_path(adapter_contract_path_str);
|
||||||
|
decoder.register_decoder_with_context::<EVMPoolState<PreCachedDB>>(
|
||||||
|
"test_protocol",
|
||||||
|
decoder_context,
|
||||||
|
);
|
||||||
|
|
||||||
|
// NOTE: Once tycho-simulation is updated, you can use the new API like this:
|
||||||
|
// use tycho_simulation::protocol::models::DecoderContext;
|
||||||
|
// let context = DecoderContext::new().vm_adapter_path(adapter_contract_path_str);
|
||||||
|
// decoder.register_decoder_with_context::<EVMPoolState<PreCachedDB>>("test_protocol", context);
|
||||||
|
|
||||||
// Mock a stream message, with only a Snapshot and no deltas
|
// Mock a stream message, with only a Snapshot and no deltas
|
||||||
let mut states: HashMap<String, ComponentWithState> = HashMap::new();
|
let mut states: HashMap<String, ComponentWithState> = HashMap::new();
|
||||||
@@ -325,9 +377,6 @@ fn validate_state(
|
|||||||
.or_insert_with(|| comp.tokens.clone());
|
.or_insert_with(|| comp.tokens.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where we get blocked. Currently, Tycho Simulation expects the runtime to be
|
|
||||||
// prebuild and accessible from TychoSim - we should allow passing it when parsing the block
|
|
||||||
|
|
||||||
// TODO: Since we don't have balances on the VM State, we could try to use Limits, otherwise ask
|
// TODO: Since we don't have balances on the VM State, we could try to use Limits, otherwise ask
|
||||||
// the user to specify a set of values on the YAML file.
|
// the user to specify a set of values on the YAML file.
|
||||||
for (id, state) in block_msg.states.iter() {
|
for (id, state) in block_msg.states.iter() {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ pub enum RpcError {
|
|||||||
impl fmt::Display for RpcError {
|
impl fmt::Display for RpcError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RpcError::ClientError(msg) => write!(f, "RPC client error: {}", msg),
|
RpcError::ClientError(msg) => write!(f, "RPC client error: {msg}"),
|
||||||
RpcError::ResponseError(msg) => write!(f, "RPC response error: {}", msg),
|
RpcError::ResponseError(msg) => write!(f, "RPC response error: {msg}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,10 +129,12 @@ impl TychoRunner {
|
|||||||
debug!("Received termination message, stopping RPC server...");
|
debug!("Received termination message, stopping RPC server...");
|
||||||
cmd.kill()
|
cmd.kill()
|
||||||
.expect("Failed to kill RPC server");
|
.expect("Failed to kill RPC server");
|
||||||
|
let _ = cmd.wait();
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Channel closed, terminate anyway
|
// Channel closed, terminate anyway
|
||||||
let _ = cmd.kill();
|
let _ = cmd.kill();
|
||||||
|
let _ = cmd.wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -160,7 +162,7 @@ impl TychoRunner {
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let reader = BufReader::new(stdout);
|
let reader = BufReader::new(stdout);
|
||||||
for line in reader.lines().map_while(Result::ok) {
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
println!("{}", line);
|
println!("{line}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -169,7 +171,7 @@ impl TychoRunner {
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let reader = BufReader::new(stderr);
|
let reader = BufReader::new(stderr);
|
||||||
for line in reader.lines().map_while(Result::ok) {
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
eprintln!("{}", line);
|
eprintln!("{line}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ pub fn build_spkg(yaml_file_path: &PathBuf, initial_block: u64) -> miette::Resul
|
|||||||
.expect("Version not found on YAML");
|
.expect("Version not found on YAML");
|
||||||
|
|
||||||
let package_version = binding.as_str().unwrap_or("");
|
let package_version = binding.as_str().unwrap_or("");
|
||||||
|
let spkg_name = format!("{parent_dir}/{package_name}-{package_version}.spkg");
|
||||||
let spkg_name = format!("{}/{}-{}.spkg", parent_dir, package_name, package_version);
|
|
||||||
|
|
||||||
// Write the modified YAML back to the file
|
// Write the modified YAML back to the file
|
||||||
let yaml_string = serde_yaml::to_string(&data).into_diagnostic()?;
|
let yaml_string = serde_yaml::to_string(&data).into_diagnostic()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user