Allow for substrate-based nodes to be managed by the user

This commit is contained in:
Omar Abdulla
2025-10-14 19:39:13 +03:00
parent b40e5dea9a
commit cf538276da
5 changed files with 48 additions and 51 deletions
+18
View File
@@ -812,6 +812,24 @@ pub struct EthRpcConfiguration {
value_parser = parse_duration
)]
pub start_timeout_ms: Duration,
/// Specifies the connection string of an existing node that's not managed by the framework.
///
/// If this argument is specified then the framework will not spawn certain nodes itself but
/// rather it will opt to using the existing node's through their provided connection strings.
///
/// This means that if `ConcurrencyConfiguration.number_of_nodes` is 10 and we only specify the
/// connection strings of 2 nodes here, then nodes 0 and 1 will use the provided connection
/// strings and nodes 2 through 10 (exclusive) will all be spawned and managed by the framework.
///
/// Thus, if you want all of the transactions and tests to happen against the node that you
/// spawned and manage then you need to specify a `ConcurrencyConfiguration.number_of_nodes` of
/// 1.
#[clap(
id = "revive-dev-node.existing-rpc-url",
long = "revive-dev-node.existing-rpc-url"
)]
pub existing_rpc_url: Vec<String>,
}
/// A set of configuration parameters for the genesis.
@@ -31,8 +31,8 @@ use revive_dt_common::{
use revive_dt_format::{
metadata::{ContractInstance, ContractPathAndIdent},
steps::{
AllocateAccountStep, BalanceAssertionStep, Calldata, EtherValue, FunctionCallStep, Method,
RepeatStep, Step, StepAddress, StepIdx, StepPath, StorageEmptyAssertionStep,
AllocateAccountStep, Calldata, EtherValue, FunctionCallStep, Method, RepeatStep, Step,
StepIdx, StepPath,
},
traits::{ResolutionContext, ResolverApi},
};
@@ -428,26 +428,6 @@ where
Ok(())
}
#[instrument(level = "info", skip_all, fields(driver_id = self.driver_id))]
pub async fn execute_balance_assertion(
&mut self,
_: &StepPath,
_: &BalanceAssertionStep,
) -> anyhow::Result<usize> {
// Kept empty intentionally for the benchmark driver.
Ok(1)
}
#[instrument(level = "info", skip_all, fields(driver_id = self.driver_id), err(Debug))]
async fn execute_storage_empty_assertion_step(
&mut self,
_: &StepPath,
_: &StorageEmptyAssertionStep,
) -> Result<usize> {
// Kept empty intentionally for the benchmark driver.
Ok(1)
}
#[instrument(level = "info", skip_all, fields(driver_id = self.driver_id), err(Debug))]
async fn execute_repeat_step(
&mut self,
@@ -671,33 +651,6 @@ where
Ok((address, abi, receipt))
}
#[instrument(level = "info", fields(driver_id = self.driver_id), skip_all)]
async fn step_address_auto_deployment(
&mut self,
step_address: &StepAddress,
) -> Result<Address> {
match step_address {
StepAddress::Address(address) => Ok(*address),
StepAddress::ResolvableAddress(resolvable) => {
let Some(instance) = resolvable
.strip_suffix(".address")
.map(ContractInstance::new)
else {
bail!("Not an address variable");
};
self.get_or_deploy_contract_instance(
&instance,
FunctionCallStep::default_caller_address(),
None,
None,
)
.await
.map(|v| v.0)
}
}
}
// endregion:Contract Deployment
// region:Resolution & Resolver
+10
View File
@@ -207,6 +207,7 @@ impl Platform for KitchensinkPolkavmResolcPlatform {
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
context,
&[],
);
let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>)
@@ -270,6 +271,7 @@ impl Platform for KitchensinkRevmSolcPlatform {
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
context,
&[],
);
let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>)
@@ -324,10 +326,13 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context);
let eth_rpc_configuration = AsRef::<EthRpcConfiguration>::as_ref(&context);
let revive_dev_node_path = revive_dev_node_configuration.path.clone();
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
let eth_rpc_connection_strings = eth_rpc_configuration.existing_rpc_url.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -335,6 +340,7 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform {
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
Some(revive_dev_node_consensus),
context,
&eth_rpc_connection_strings,
);
let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>)
@@ -389,10 +395,13 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
) -> anyhow::Result<JoinHandle<anyhow::Result<Box<dyn EthereumNode + Send + Sync>>>> {
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
let revive_dev_node_configuration = AsRef::<ReviveDevNodeConfiguration>::as_ref(&context);
let eth_rpc_configuration = AsRef::<EthRpcConfiguration>::as_ref(&context);
let revive_dev_node_path = revive_dev_node_configuration.path.clone();
let revive_dev_node_consensus = revive_dev_node_configuration.consensus.clone();
let eth_rpc_connection_strings = eth_rpc_configuration.existing_rpc_url.clone();
let genesis = genesis_configuration.genesis()?.clone();
Ok(thread::spawn(move || {
let node = SubstrateNode::new(
@@ -400,6 +409,7 @@ impl Platform for ReviveDevNodeRevmSolcPlatform {
SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND,
Some(revive_dev_node_consensus),
context,
&eth_rpc_connection_strings,
);
let node = spawn_node(node, genesis)?;
Ok(Box::new(node) as Box<_>)
@@ -99,6 +99,7 @@ impl SubstrateNode {
context: impl AsRef<WorkingDirectoryConfiguration>
+ AsRef<EthRpcConfiguration>
+ AsRef<WalletConfiguration>,
existing_connection_strings: &[String],
) -> Self {
let working_directory_path =
AsRef::<WorkingDirectoryConfiguration>::as_ref(&context).as_path();
@@ -112,12 +113,17 @@ impl SubstrateNode {
let base_directory = substrate_directory.join(id.to_string());
let logs_directory = base_directory.join(Self::LOGS_DIRECTORY);
let rpc_url = existing_connection_strings
.get(id as usize)
.cloned()
.unwrap_or_default();
Self {
id,
node_binary: node_path,
eth_proxy_binary: eth_rpc_path.to_path_buf(),
export_chainspec_command: export_chainspec_command.to_string(),
rpc_url: String::new(),
rpc_url,
base_directory,
logs_directory,
substrate_process: None,
@@ -130,6 +136,10 @@ impl SubstrateNode {
}
fn init(&mut self, _: Genesis) -> anyhow::Result<&mut Self> {
if !self.rpc_url.is_empty() {
return Ok(self);
}
let _ = remove_dir_all(self.base_directory.as_path());
let _ = clear_directory(&self.base_directory);
let _ = clear_directory(&self.logs_directory);
@@ -158,6 +168,10 @@ impl SubstrateNode {
}
fn spawn_process(&mut self) -> anyhow::Result<()> {
if !self.rpc_url.is_empty() {
return Ok(());
}
let substrate_rpc_port = Self::BASE_SUBSTRATE_RPC_PORT + self.id as u16;
let proxy_rpc_port = Self::BASE_PROXY_RPC_PORT + self.id as u16;
@@ -772,6 +786,7 @@ mod tests {
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
&context,
&[],
);
node.init(context.genesis_configuration.genesis().unwrap().clone())
.expect("Failed to initialize the node")
@@ -838,6 +853,7 @@ mod tests {
SubstrateNode::KITCHENSINK_EXPORT_CHAINSPEC_COMMAND,
None,
&context,
&[],
);
// Call `init()`
+1 -1
View File
@@ -44,7 +44,7 @@ where
// requests at any point of time and no more than that. This is done in an effort to stabilize
// the framework from some of the interment issues that we've been seeing related to RPC calls.
static GLOBAL_CONCURRENCY_LIMITER_LAYER: LazyLock<ConcurrencyLimiterLayer> =
LazyLock::new(|| ConcurrencyLimiterLayer::new(1000));
LazyLock::new(|| ConcurrencyLimiterLayer::new(500));
let client = ClientBuilder::default()
.layer(GLOBAL_CONCURRENCY_LIMITER_LAYER.clone())