Skip to content
Draft
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 Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,7 @@ docs: mermaid-init.js mermaid.min.js ## 📚 Generate the documentation

docs-serve: mermaid-init.js mermaid.min.js ## 📚 Generate and serve the documentation
mdbook serve --open

test-reorgs: ## 🧪 Test reorg scenarios
cargo build --bin ethrex
cd tooling/reorgs && cargo run
55 changes: 55 additions & 0 deletions tooling/reorgs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ async fn main() {
info!("");

run_test(&cmd_path, no_reorgs_full_sync_smoke_test).await;
run_test(&cmd_path, snap_sync_smoke_test).await;
run_test(&cmd_path, test_reorg_back_to_base).await;

// This test is flaky 50% of the time, check that it runs correctly multiple times in a row
Expand Down Expand Up @@ -103,6 +104,60 @@ async fn no_reorgs_full_sync_smoke_test(simulator: Arc<Mutex<Simulator>>) {
node1.update_forkchoice(&base_chain).await;
}

async fn snap_sync_smoke_test(simulator: Arc<Mutex<Simulator>>) {
let mut simulator = simulator.lock().await;
// Initcode for deploying a contract that receives two `bytes32` parameters and sets `storage[param0] = param1`
let contract_deploy_bytecode = hex::decode("656020355f35555f526006601af3").unwrap().into();
let signer: Signer = LocalSigner::new(
"941e103320615d394a55708be13e45994c7d93b932b064dbcb2b511fe3254e2e"
.parse()
.unwrap(),
)
.into();

let slot_key0 = U256::from(42);
let slot_value0 = U256::from(1163);
let slot_key1 = U256::from(25);
let slot_value1 = U256::from(7474);

let node1 = simulator.start_node().await;

// Create a chain with a few empty blocks
let mut base_chain = simulator.get_base_chain();

base_chain = node1.extend_chain(base_chain, 10).await;

// Send a deploy tx for a contract which receives: `(bytes32 key, bytes32 value)` as parameters
let contract_address = node1
.send_contract_deploy(&signer, contract_deploy_bytecode)
.await;

// Set another storage slot in the contract in node1
let calldata0 = [slot_key0.to_big_endian(), slot_value0.to_big_endian()]
.concat()
.into();
node1.send_call(&signer, contract_address, calldata0).await;

base_chain = node1.extend_chain(base_chain, 10).await;
// Set another storage slot in the contract in node1
let calldata1 = [slot_key1.to_big_endian(), slot_value1.to_big_endian()]
.concat()
.into();
node1.send_call(&signer, contract_address, calldata1).await;

base_chain = node1.extend_chain(base_chain, 1000).await;

let node0 = simulator.start_snapsync_node().await;

node0.update_forkchoice(&base_chain).await;

// Check the storage slots are as expected after the reorg
let value_slot0 = node0.get_storage_at(contract_address, slot_key0).await;
assert_eq!(value_slot0, slot_value0);
let value_slot1 = node0.get_storage_at(contract_address, slot_key1).await;
assert_eq!(value_slot1, slot_value1);
}

async fn test_reorg_back_to_base(simulator: Arc<Mutex<Simulator>>) {
let mut simulator = simulator.lock().await;

Expand Down
87 changes: 86 additions & 1 deletion tooling/reorgs/src/simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,91 @@ impl Simulator {
self.get_node(n)
}

pub async fn start_snapsync_node(&mut self) -> Node {
let n = self.configs.len();
let test_name = &self.test_name;
info!(node = n, "Starting node");
let mut opts = self.base_opts.clone();
opts.datadir = format!("data/{test_name}/node{n}").into();

opts.http_port = get_next_port().to_string();
opts.authrpc_port = get_next_port().to_string();

// These are one TCP and one UDP
let p2p_port = get_next_port();
opts.p2p_port = p2p_port.to_string();
opts.discovery_port = p2p_port.to_string();

opts.syncmode = SyncMode::Snap;

let _ = std::fs::remove_dir_all(&opts.datadir);
std::fs::create_dir_all(&opts.datadir).expect("Failed to create data directory");

let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let logs_file_path = format!("data/{test_name}/node{n}_{now}.log");
let logs_file = File::create(&logs_file_path).expect("Failed to create logs file");

let cancel = CancellationToken::new();

self.configs.push(opts.clone());
self.cancellation_tokens.push(cancel.clone());

let mut cmd = Command::new(&self.cmd_path);
cmd.args([
format!("--http.addr={}", opts.http_addr),
format!("--http.port={}", opts.http_port),
format!("--authrpc.addr={}", opts.authrpc_addr),
format!("--authrpc.port={}", opts.authrpc_port),
format!("--p2p.port={}", opts.p2p_port),
format!("--discovery.port={}", opts.discovery_port),
format!("--datadir={}", opts.datadir.display()),
format!("--network={}", self.genesis_path.display()),
format!("--syncmode={:?}", opts.syncmode).to_lowercase(),
"--force".to_string(),
])
.stdin(Stdio::null())
.stdout(logs_file.try_clone().unwrap())
.stderr(logs_file);

if !self.enodes.is_empty() {
cmd.arg(format!("--bootnodes={}", self.enodes.join(",")));
}

let child = cmd.spawn().expect("Failed to start ethrex process");

let logs_file = File::open(&logs_file_path).expect("Failed to open logs file");
let enode =
tokio::time::timeout(Duration::from_secs(5), wait_for_initialization(logs_file))
.await
.expect("node initialization timed out");
self.enodes.push(enode);

tokio::spawn(async move {
let mut child = child;
tokio::select! {
_ = cancel.cancelled() => {
if let Some(pid) = child.id() {
// NOTE: we use SIGTERM instead of child.kill() so sockets are closed
signal::kill(Pid::from_raw(pid as i32), Signal::SIGTERM).unwrap();
}
}
res = child.wait() => {
assert!(res.unwrap().success());
}
}
});

info!(
"Started node {n} at http://{}:{}",
opts.http_addr, opts.http_port
);

self.get_node(n)
}

pub fn stop(&self) {
for token in &self.cancellation_tokens {
token.cancel();
Expand Down Expand Up @@ -378,7 +463,7 @@ impl Node {
let sender_address = signer.address();
let nonce = self
.rpc_client
.get_nonce(sender_address, BlockIdentifier::Tag(BlockTag::Latest))
.get_nonce(sender_address, BlockIdentifier::Tag(BlockTag::Pending))
.await
.unwrap();
let tx = EIP1559Transaction {
Expand Down
Loading