Skip to content
Merged
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,066 changes: 2,665 additions & 1,401 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,10 @@ alloy-tx-macros = { git = "https://github.com/alloy-rs/alloy", tag = "v1.0.37" }
# 100% CPU spin). Upstream issue: https://github.com/jmagnuson/linemux/issues/57
linemux = { git = "https://github.com/Galxe/linemux.git", rev = "0194e0222134c587dcd50039a1ff9c8a14fae550" }

# greth's own `[patch.crates-io]` is IGNORED once greth is consumed as a git
# dependency (only the top-level workspace's patches apply), so replicate its
# single active redirect here: collapse the transitive crates.io
# `reth-primitives-traits` onto greth's in-tree crate so the whole graph
# unifies on one `reth-primitives-traits`.
reth-primitives-traits = { git = "https://github.com/Galxe/gravity-reth", rev = "bc817c642c9c3816cc4e22754e13e3c9633419dd" }

27 changes: 18 additions & 9 deletions bin/gravity_node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ futures-util = "0.3.30"
clap = { version = "4.5.17", features = ["derive", "env"] }
futures = "0.3.30"
dirs = "5.0.1"
tokio = "1.40.0"
# `taskdump` is required because the SDK builds with `--cfg tokio_unstable`
# (needed by aptos-runtimes), which activates greth's
# `#[cfg(tokio_unstable)] handle_tokio_dump` in reth-node-metrics; that calls
# `Handle::dump()`, gated on `tokio_unstable + feature="taskdump"`. greth never
# enables it (it never sets tokio_unstable standalone), so we enable it here.
tokio = { version = "1.40.0", features = ["taskdump"] }
tokio-stream = "0.1.16"
eyre = "0.6.12"
tracing = "0.1.40"
Expand All @@ -33,15 +38,19 @@ sha2.workspace = true
bincode = "1.3"
time = "0.3.36"
anyhow = "1.0.87"
greth = { git = "https://github.com/Galxe/gravity-reth", rev = "b49b4864aeaa3c35c6871a77d7133bb9486edbf1" }
# greth main @ PR #414 (block-gas last gate at Beta; includes #413/#412/#410)
greth = { git = "https://github.com/Galxe/gravity-reth", rev = "bc817c642c9c3816cc4e22754e13e3c9633419dd" }
reqwest = "0.12.9"
alloy-primitives = { version = "=1.3.1", default-features = false, features = ["map-foldhash"] }
alloy-eips = { version = "^1.0.37", default-features = false }
alloy-consensus = { version = "=1.0.37" }
alloy-genesis = { version = "=1.0.37", default-features = false }
alloy-serde = { version = "=1.0.37" }
alloy-transport-http = "=1.0.37"
alloy-rpc-types-eth = "=1.0.37"
# Aligned with gravity-reth v2.3.0 (alloy-primitives 1.6.0, alloy-* 2.0.5) so
# gravity_node's own alloy types unify with the ones re-exported through greth.
# Kept as caret (not `=`) to share a single resolved version with greth.
alloy-primitives = { version = "1.6.0", default-features = false, features = ["map-foldhash"] }
alloy-eips = { version = "2.0.5", default-features = false }
alloy-consensus = { version = "2.0.5" }
alloy-genesis = { version = "2.0.5", default-features = false }
alloy-serde = { version = "2.0.5" }
alloy-transport-http = "2.0.5"
alloy-rpc-types-eth = "2.0.5"
async-trait.workspace = true
api.workspace = true
gaptos = { workspace = true, features = ["gcp-secret-manager"] }
Expand Down
24 changes: 18 additions & 6 deletions bin/gravity_node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use greth::{
reth_node_builder::{NodeBuilder, WithLaunchContext},
reth_node_core::args::LogArgs,
reth_node_ethereum::{consensus::EthBeaconConsensus, EthEvmConfig, EthereumNode},
reth_tracing::FileWorkerGuard,
reth_tracing::TracingGuards,
};
use std::{
collections::BTreeMap,
Expand Down Expand Up @@ -155,10 +155,20 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cl
self.logs.log_file_directory =
self.logs.log_file_directory.join(self.chain.chain.to_string());

// greth keeps the node-subcommand default (5 files) outside LogArgs, so `--log.file.*`
// are otherwise accepted while the file layer is never installed (see its app.rs).
if matches!(self.command, Commands::Node(_)) {
self.logs.apply_node_defaults();
}

let _guard = self.init_tracing()?;
debug!(target: "reth::cli", "Initialized tracing, log directory: {}, log level {:?}", self.logs.log_file_directory, self.logs.verbosity);

let runner = CliRunner::try_default_runtime()?;
// reth v2.3.0: init/init-state execute() now take a Runtime; db/prune
// execute() take a CliContext supplied via the *_command_until_exit runner
// methods (see gravity-reth crates/ethereum/cli/src/app.rs).
let rt = runner.runtime();
let components = |spec: Arc<C::ChainSpec>| {
(EthEvmConfig::ethereum(spec.clone()), Arc::new(EthBeaconConsensus::new(spec)))
};
Expand All @@ -171,18 +181,20 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cl
}
Commands::Init(command) => {
println!("Running init command");
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>(rt))
}
Commands::InitState(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>(rt))
}
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
Commands::Db(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<EthereumNode>())
runner.run_blocking_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
}
Commands::P2P(command) => runner.run_until_ctrl_c(command.execute::<EthereumNode>()),
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<EthereumNode>()),
Commands::Prune(command) => {
runner.run_command_until_exit(|ctx| command.execute::<EthereumNode>(ctx))
}
Commands::Stage(command) => {
println!("Running stage command");
runner.run_command_until_exit(|ctx| {
Expand All @@ -197,7 +209,7 @@ impl<C: ChainSpecParser<ChainSpec = ChainSpec>, Ext: clap::Args + fmt::Debug> Cl
///
/// If file logging is enabled, this function returns a guard that must be kept alive to ensure
/// that all logs are flushed to disk.
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
pub fn init_tracing(&self) -> eyre::Result<TracingGuards> {
let guard = self.logs.init_tracing()?;
Ok(guard)
}
Expand Down
1 change: 1 addition & 0 deletions bin/gravity_node/src/reth_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub(crate) type RethTransactionPool = greth::reth_transaction_pool::Pool<
greth::reth_transaction_pool::EthTransactionValidator<
RethBlockChainProvider,
greth::reth_transaction_pool::EthPooledTransaction,
greth::reth_node_ethereum::EthEvmConfig,
>,
>,
greth::reth_transaction_pool::CoinbaseTipOrdering<
Expand Down
102 changes: 96 additions & 6 deletions cluster/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -449,14 +449,44 @@ START_SCRIPT
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$SCRIPT_DIR/.."

# Wait until the kernel has reaped the process. greth v2.3+ can keep the
# RocksDB LOCK held for several seconds after SIGTERM while flushing; if we
# delete the pid file and return early, e2e restart races the lock and dies
# with "Resource temporarily unavailable".
wait_pid_gone() {
local pid="$1"
local max_iters="$2"
local i
for i in $(seq 1 "$max_iters"); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.5
done
return 1
}

if [ -e "${WORKSPACE}/script/node.pid" ]; then
pid=$(cat "${WORKSPACE}/script/node.pid")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
echo "Stopped node (PID: $pid)"
kill "$pid" 2>/dev/null || true
# ~30s graceful (SIGTERM)
if wait_pid_gone "$pid" 60; then
echo "Stopped node (PID: $pid)"
else
echo "Node (PID: $pid) still alive after SIGTERM; sending SIGKILL"
kill -9 "$pid" 2>/dev/null || true
# ~20s after SIGKILL for process to disappear
if wait_pid_gone "$pid" 40; then
echo "Stopped node (PID: $pid, forced)"
else
echo "ERROR: node PID $pid still alive after SIGKILL" >&2
fi
fi
else
echo "Node not running (stale PID file)"
fi
# Only drop the pid file after the process is gone (or we gave up).
rm -f "${WORKSPACE}/script/node.pid"
else
echo "No PID file found"
Expand Down Expand Up @@ -573,14 +603,44 @@ START_SCRIPT
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$SCRIPT_DIR/.."

# Wait until the kernel has reaped the process. greth v2.3+ can keep the
# RocksDB LOCK held for several seconds after SIGTERM while flushing; if we
# delete the pid file and return early, e2e restart races the lock and dies
# with "Resource temporarily unavailable".
wait_pid_gone() {
local pid="$1"
local max_iters="$2"
local i
for i in $(seq 1 "$max_iters"); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.5
done
return 1
}

if [ -e "${WORKSPACE}/script/node.pid" ]; then
pid=$(cat "${WORKSPACE}/script/node.pid")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
echo "Stopped node (PID: $pid)"
kill "$pid" 2>/dev/null || true
# ~30s graceful (SIGTERM)
if wait_pid_gone "$pid" 60; then
echo "Stopped node (PID: $pid)"
else
echo "Node (PID: $pid) still alive after SIGTERM; sending SIGKILL"
kill -9 "$pid" 2>/dev/null || true
# ~20s after SIGKILL for process to disappear
if wait_pid_gone "$pid" 40; then
echo "Stopped node (PID: $pid, forced)"
else
echo "ERROR: node PID $pid still alive after SIGKILL" >&2
fi
fi
else
echo "Node not running (stale PID file)"
fi
# Only drop the pid file after the process is gone (or we gave up).
rm -f "${WORKSPACE}/script/node.pid"
else
echo "No PID file found"
Expand Down Expand Up @@ -706,14 +766,44 @@ START_SCRIPT
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE="$SCRIPT_DIR/.."

# Wait until the kernel has reaped the process. greth v2.3+ can keep the
# RocksDB LOCK held for several seconds after SIGTERM while flushing; if we
# delete the pid file and return early, e2e restart races the lock and dies
# with "Resource temporarily unavailable".
wait_pid_gone() {
local pid="$1"
local max_iters="$2"
local i
for i in $(seq 1 "$max_iters"); do
if ! kill -0 "$pid" 2>/dev/null; then
return 0
fi
sleep 0.5
done
return 1
}

if [ -e "${WORKSPACE}/script/node.pid" ]; then
pid=$(cat "${WORKSPACE}/script/node.pid")
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
echo "Stopped node (PID: $pid)"
kill "$pid" 2>/dev/null || true
# ~30s graceful (SIGTERM)
if wait_pid_gone "$pid" 60; then
echo "Stopped node (PID: $pid)"
else
echo "Node (PID: $pid) still alive after SIGTERM; sending SIGKILL"
kill -9 "$pid" 2>/dev/null || true
# ~20s after SIGKILL for process to disappear
if wait_pid_gone "$pid" 40; then
echo "Stopped node (PID: $pid, forced)"
else
echo "ERROR: node PID $pid still alive after SIGKILL" >&2
fi
fi
else
echo "Node not running (stale PID file)"
fi
# Only drop the pid file after the process is gone (or we gave up).
rm -f "${WORKSPACE}/script/node.pid"
else
echo "No PID file found"
Expand Down
26 changes: 20 additions & 6 deletions cluster/stop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,36 @@ stop_node() {

log_info "Stopping $node_id (PID: $pid)..."
kill "$pid" 2>/dev/null || true

# Wait for graceful shutdown
for i in {1..10}; do

# Wait until the process is really gone before dropping the pid file.
# greth v2.3+ can hold the RocksDB LOCK for several seconds after SIGTERM
# while flushing; returning early races the next start
# ("Resource temporarily unavailable" on .../db/state/LOCK).
# ~30s graceful
for i in {1..60}; do
if ! kill -0 "$pid" 2>/dev/null; then
rm -f "$pid_file"
log_info "$node_id stopped"
return 0
fi
sleep 0.5
done
# Force kill if still running

# Force kill if still running, then wait again for the kernel to reap it.
log_warn "$node_id: Force killing..."
kill -9 "$pid" 2>/dev/null || true
for i in {1..40}; do
if ! kill -0 "$pid" 2>/dev/null; then
rm -f "$pid_file"
log_info "$node_id stopped (forced)"
return 0
fi
sleep 0.5
done

# Last resort: drop pid bookkeeping so callers are not stuck, but warn.
rm -f "$pid_file"
log_info "$node_id stopped (forced)"
log_error "$node_id: PID $pid still alive after SIGKILL"
}

# Main
Expand Down
7 changes: 7 additions & 0 deletions gravity_e2e/cluster_test_cases/prague/genesis.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
# !! pragueTime must be > genesis_timestamp_secs so the EIP-2935 deployment
# hook (pipe-exec eip_2935.rs gate parent_ts < pragueTime <= current_ts)
# fires on block 1. Change one, change the other.
#
# !! betaTime (gravity-reth #412): until Beta, filter_invalid_txs wholesale-rejects
# type-4 and from/to-delegated traffic (EIP-7702 emergency lockdown). Missing
# betaTime → lockdown forever (fail-closed). This suite exercises Prague 7702
# behaviour, so release lockdown at the same timestamp as pragueTime.

[dependencies.genesis_contracts]
repo = "https://github.com/Galxe/gravity_chain_core_contracts.git"
Expand All @@ -29,6 +34,8 @@ initial_locked_until_micros = 1798848000000000

[genesis.hardforks]
pragueTime = 1775664001
# Same wall-clock as pragueTime: Prague protocol + Beta lockdown release together.
betaTime = 1775664001

[genesis.faucet]
address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
Expand Down
Loading
Loading