Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/fuels-test-helpers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ tempfile = { workspace = true, default-features = false }
tokio = { workspace = true, default-features = false }
which = { workspace = true, default-features = false }

[dev-dependencies]
testcontainers = { version = "0.23.0", features = ["blocking"] }
ethers = { version = "=2.0.8" }

[features]
default = ["fuels-accounts", "std"]
std = ["fuels-accounts?/std", "fuels-core/std", "fuel-core-chain-config/std"]
Expand Down
59 changes: 59 additions & 0 deletions packages/fuels-test-helpers/src/fuel_bin_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@ use tokio::{process::Command, spawn, task::JoinHandle, time::sleep};

use crate::node_types::{DbType, NodeConfig, Trigger};

#[derive(Debug)]
pub struct RelayerConfig {
pub relayer: String,
pub relayer_v2_listening_contracts: String,
}

#[derive(Debug)]
pub(crate) struct ExtendedConfig {
pub node_config: NodeConfig,
pub chain_config: ChainConfig,
pub state_config: StateConfig,
pub snapshot_dir: TempDir,
pub relayer_config: Option<RelayerConfig>,
}

impl ExtendedConfig {
Expand Down Expand Up @@ -74,6 +81,15 @@ impl ExtendedConfig {
}
};

if let Some(relayer_config) = &self.relayer_config {
args.push("--enable-relayer".to_string());
args.push("--relayer".to_string());
args.push(relayer_config.relayer.clone());

args.push("--relayer-v2-listening-contracts".to_string());
args.push(relayer_config.relayer_v2_listening_contracts.clone());
}

let body_limit = self.node_config.graphql_request_body_bytes_limit;
args.push(format!("--graphql-request-body-bytes-limit={body_limit}"));

Expand Down Expand Up @@ -150,6 +166,49 @@ impl FuelService {
state_config,
chain_config,
snapshot_dir: tempdir()?,
relayer_config: None,
};

let addr = extended_config.node_config.addr;
let handle = run_node(extended_config).await?;
server_health_check(addr).await?;

Ok(FuelService {
bound_address,
handle,
})
}

pub async fn new_node_with_relayer(
node_config: NodeConfig,
chain_config: ChainConfig,
state_config: StateConfig,
relayer_config: RelayerConfig,
) -> FuelResult<Self> {
let requested_port = node_config.addr.port();

let bound_address = match requested_port {
0 => get_socket_address()?,
_ if is_free(requested_port) => node_config.addr,
_ => {
return Err(error!(
IO,
"could not find a free port to start a fuel node"
))
}
};

let node_config = NodeConfig {
addr: bound_address,
..node_config
};

let extended_config = ExtendedConfig {
node_config,
state_config,
chain_config,
snapshot_dir: tempdir()?,
relayer_config: Some(relayer_config),
};

let addr = extended_config.node_config.addr;
Expand Down
136 changes: 135 additions & 1 deletion packages/fuels-test-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ mod node_types;

#[cfg(not(feature = "fuel-core-lib"))]
pub(crate) mod fuel_bin_service;
#[cfg(not(feature = "fuel-core-lib"))]
use fuel_bin_service::RelayerConfig;

#[cfg(feature = "fuels-accounts")]
mod accounts;
Expand Down Expand Up @@ -153,6 +155,40 @@ pub async fn setup_test_provider(
Provider::from(address).await
}

#[cfg(not(feature = "fuel-core-lib"))]
pub async fn setup_test_provider_with_relayer(
coins: Vec<Coin>,
messages: Vec<Message>,
node_config: Option<NodeConfig>,
chain_config: Option<ChainConfig>,
relayer_config: RelayerConfig,
) -> Result<Provider> {
let node_config = node_config.unwrap_or_default();
let chain_config = chain_config.unwrap_or_else(testnet_chain_config);

let coin_configs = into_coin_configs(coins);
let message_configs = into_message_configs(messages);

let state_config = StateConfig {
coins: coin_configs,
messages: message_configs,
..StateConfig::local_testnet()
};

let srv =
FuelService::start_with_relayer(node_config, chain_config, state_config, relayer_config)
.await?;

let address = srv.bound_address();

tokio::spawn(async move {
let _own_the_handle = srv;
let () = futures::future::pending().await;
});

Provider::from(address).await
}

// Testnet ChainConfig with increased tx size and contract size limits
fn testnet_chain_config() -> ChainConfig {
let mut consensus_parameters = ConsensusParameters::default();
Expand All @@ -173,11 +209,22 @@ pub fn generate_random_salt() -> [u8; 32] {

#[cfg(test)]
mod tests {
use std::net::{Ipv4Addr, SocketAddr};
use std::{
net::{Ipv4Addr, SocketAddr},
time::{Duration, Instant},
};

use fuel_tx::{ConsensusParameters, ContractParameters, FeeParameters, TxParameters};
use fuels_core::types::bech32::FUEL_BECH32_HRP;

use testcontainers::{
core::{IntoContainerPort, WaitFor},
runners::AsyncRunner,
GenericImage, ImageExt,
};

use ethers::providers::{Http, Provider};

use super::*;

#[tokio::test]
Expand Down Expand Up @@ -382,4 +429,91 @@ mod tests {
);
Ok(())
}

#[tokio::test]
#[cfg(not(feature = "fuel-core-lib"))]
async fn test_setup_test_provider_with_relayer() -> Result<()> {
let container = GenericImage::new(
"ghcr.io/foundry-rs/foundry",
"nightly-4351742481c98adaa9ca3e8642e619aa986b3cee",
)
.with_wait_for(WaitFor::message_on_stdout("Listening on 0.0.0.0:8545"))
.with_exposed_port(8545.tcp())
.with_entrypoint("anvil")
.with_cmd(["--host", "0.0.0.0", "--slots-in-an-epoch", "1"])
.with_mapped_port(8545, 8545.into())
.start()
.await
.expect("Anvil did not start");

let eth_provider = Provider::<Http>::try_from("http://127.0.0.1:8545")
.expect("Could not instantiate ethereum provider");

let socket = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 4000);
let node_config = NodeConfig {
addr: socket,
debug: true,
block_production: Trigger::Interval {
block_time: Duration::from_secs(1),
},
..NodeConfig::default()
};

let relayer_config = RelayerConfig {
relayer: "http://localhost:8545".to_string(),
relayer_v2_listening_contracts: "0x0000000000000000000000000000000000000000"
.to_string(),
};

let fuel_provider = setup_test_provider_with_relayer(
vec![],
vec![],
Some(node_config.clone()),
None,
relayer_config,
)
.await?;

let node_info = fuel_provider
.node_info()
.await
.expect("Failed to retrieve node info!");

assert_eq!(fuel_provider.url(), format!("http://127.0.0.1:4000"));
assert_eq!(node_info.utxo_validation, node_config.utxo_validation);

// Mine some eth blocks
let _: () = eth_provider
// Magic number: 128 is meant to be 4 times an epoch, so it finalizes eth blocks
.request("anvil_mine", vec![ethers::types::U256::from(128u64)])
.await
.expect("Could not mine blocks on Ethereum chain");

let start_time = Instant::now();
let timeout = Duration::from_secs(10);
loop {
// Check if we've exceeded the timeout
if start_time.elapsed() > timeout {
return Err("Timeout reached while waiting for positive DA height".into());
}

let height = fuel_provider.latest_block_height().await?;

let latest_block = fuel_provider
.block_by_height(height.into())
.await?
.ok_or("Latest block not found")?;

if latest_block.header.da_height > 0 {
break;
}

// Wait for the production of a new fuel block
tokio::time::sleep(Duration::from_secs(1)).await;
}

let _ = container.stop().await;

Ok(())
}
}
25 changes: 24 additions & 1 deletion packages/fuels-test-helpers/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use fuel_core_services::State;
use fuels_core::types::errors::{error, Result};

#[cfg(not(feature = "fuel-core-lib"))]
use crate::fuel_bin_service::FuelService as BinFuelService;
use crate::fuel_bin_service::{FuelService as BinFuelService, RelayerConfig};
use crate::NodeConfig;

pub struct FuelService {
Expand Down Expand Up @@ -43,6 +43,29 @@ impl FuelService {
})
}

#[cfg(not(feature = "fuel-core-lib"))]
pub async fn start_with_relayer(
node_config: NodeConfig,
chain_config: ChainConfig,
state_config: StateConfig,
relayer_config: RelayerConfig,
) -> Result<Self> {
let service = BinFuelService::new_node_with_relayer(
node_config,
chain_config,
state_config,
relayer_config,
)
.await?;

let bound_address = service.bound_address;

Ok(FuelService {
service,
bound_address,
})
}

pub async fn stop(&self) -> Result<State> {
#[cfg(feature = "fuel-core-lib")]
let result = self.service.send_stop_signal_and_await_shutdown().await;
Expand Down
Loading