mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-29 10:54:24 +00:00
Merge branch 'beacon-api-electra' of https://github.com/sigp/lighthouse into ef-tests-electra
This commit is contained in:
185
.github/workflows/local-testnet.yml
vendored
185
.github/workflows/local-testnet.yml
vendored
@@ -13,87 +13,154 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
run-local-testnet:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-22.04
|
||||
- macos-12
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
# Enable portable to prevent issues with caching `blst` for the wrong CPU type
|
||||
FEATURES: portable,jemalloc
|
||||
dockerfile-ubuntu:
|
||||
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get latest version of stable Rust
|
||||
run: rustup update stable
|
||||
- name: Install geth (ubuntu)
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:ethereum/ethereum
|
||||
sudo apt-get update
|
||||
sudo apt-get install ethereum
|
||||
- name: Install geth (mac)
|
||||
if: matrix.os == 'macos-12'
|
||||
run: |
|
||||
brew tap ethereum/ethereum
|
||||
brew install ethereum
|
||||
- name: Install GNU sed & GNU grep
|
||||
if: matrix.os == 'macos-12'
|
||||
run: |
|
||||
brew install gnu-sed grep
|
||||
echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
|
||||
echo "$(brew --prefix)/opt/grep/libexec/gnubin" >> $GITHUB_PATH
|
||||
# https://github.com/actions/cache/blob/main/examples.md#rust---cargo
|
||||
- uses: actions/cache@v4
|
||||
id: cache-cargo
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
docker build --build-arg FEATURES=portable -t lighthouse:local .
|
||||
docker save lighthouse:local -o lighthouse-docker.tar
|
||||
|
||||
- name: Install lighthouse
|
||||
run: make && make install-lcli
|
||||
- name: Upload Docker image artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lighthouse-docker
|
||||
path: lighthouse-docker.tar
|
||||
retention-days: 3
|
||||
|
||||
run-local-testnet:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: dockerfile-ubuntu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository ppa:rmescandon/yq
|
||||
echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list
|
||||
sudo apt update
|
||||
sudo apt install -y kurtosis-cli yq
|
||||
kurtosis analytics disable
|
||||
|
||||
- name: Download Docker image artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: lighthouse-docker
|
||||
path: .
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load -i lighthouse-docker.tar
|
||||
|
||||
- name: Start local testnet
|
||||
run: ./start_local_testnet.sh genesis.json && sleep 60
|
||||
run: ./start_local_testnet.sh -e local -c -b false && sleep 60
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Print logs
|
||||
run: ./dump_logs.sh
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Stop local testnet
|
||||
run: ./stop_local_testnet.sh
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Clean-up testnet
|
||||
run: ./clean.sh
|
||||
- name: Stop local testnet and dump logs
|
||||
run: ./stop_local_testnet.sh local
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Start local testnet with blinded block production
|
||||
run: ./start_local_testnet.sh -p genesis.json && sleep 60
|
||||
run: ./start_local_testnet.sh -e local-blinded -c -p -b false && sleep 60
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Print logs for blinded block testnet
|
||||
run: ./dump_logs.sh
|
||||
- name: Stop local testnet and dump logs
|
||||
run: ./stop_local_testnet.sh local-blinded
|
||||
working-directory: scripts/local_testnet
|
||||
|
||||
- name: Stop local testnet with blinded block production
|
||||
run: ./stop_local_testnet.sh
|
||||
working-directory: scripts/local_testnet
|
||||
- name: Upload logs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs-local-testnet
|
||||
path: |
|
||||
scripts/local_testnet/logs
|
||||
retention-days: 3
|
||||
|
||||
doppelganger-protection-success-test:
|
||||
needs: dockerfile-ubuntu
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository ppa:rmescandon/yq
|
||||
echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list
|
||||
sudo apt update
|
||||
sudo apt install -y kurtosis-cli yq
|
||||
kurtosis analytics disable
|
||||
|
||||
- name: Download Docker image artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: lighthouse-docker
|
||||
path: .
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load -i lighthouse-docker.tar
|
||||
|
||||
- name: Run the doppelganger protection success test script
|
||||
run: |
|
||||
./doppelganger_protection.sh success
|
||||
working-directory: scripts/tests
|
||||
|
||||
- name: Upload logs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs-doppelganger-protection-success
|
||||
path: |
|
||||
scripts/local_testnet/logs
|
||||
retention-days: 3
|
||||
|
||||
doppelganger-protection-failure-test:
|
||||
needs: dockerfile-ubuntu
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo add-apt-repository ppa:rmescandon/yq
|
||||
echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list
|
||||
sudo apt update
|
||||
sudo apt install -y kurtosis-cli yq
|
||||
kurtosis analytics disable
|
||||
|
||||
- name: Download Docker image artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: lighthouse-docker
|
||||
path: .
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load -i lighthouse-docker.tar
|
||||
|
||||
- name: Run the doppelganger protection failure test script
|
||||
run: |
|
||||
./doppelganger_protection.sh failure
|
||||
working-directory: scripts/tests
|
||||
|
||||
- name: Upload logs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs-doppelganger-protection-failure
|
||||
path: |
|
||||
scripts/local_testnet/logs
|
||||
retention-days: 3
|
||||
|
||||
|
||||
# This job succeeds ONLY IF all others succeed. It is used by the merge queue to determine whether
|
||||
# a PR is safe to merge. New jobs should be added here.
|
||||
local-testnet-success:
|
||||
name: local-testnet-success
|
||||
runs-on: ubuntu-latest
|
||||
needs: ["run-local-testnet"]
|
||||
needs: [
|
||||
'dockerfile-ubuntu',
|
||||
'run-local-testnet',
|
||||
'doppelganger-protection-success-test',
|
||||
'doppelganger-protection-failure-test',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check that success job is dependent on all others
|
||||
|
||||
52
.github/workflows/test-suite.yml
vendored
52
.github/workflows/test-suite.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
LABELS: ${{ toJson(github.event.pull_request.labels) }}
|
||||
run: |
|
||||
SKIP_CI="false"
|
||||
if [ -z "${LABELS}" ]; then
|
||||
if [ -z "${LABELS}" ] || [ "${LABELS}" = "null" ]; then
|
||||
LABELS="none";
|
||||
else
|
||||
LABELS=$(echo ${LABELS} | jq -r '.[].name')
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "::set-output name=skip_ci::$SKIP_CI"
|
||||
echo "skip_ci=$SKIP_CI" >> $GITHUB_OUTPUT
|
||||
|
||||
target-branch-check:
|
||||
name: target-branch-check
|
||||
@@ -259,17 +259,6 @@ jobs:
|
||||
- name: Show cache stats
|
||||
if: env.SELF_HOSTED_RUNNERS == 'true'
|
||||
run: sccache --show-stats
|
||||
dockerfile-ubuntu:
|
||||
name: dockerfile-ubuntu
|
||||
needs: [check-labels]
|
||||
if: needs.check-labels.outputs.skip_ci != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build the root Dockerfile
|
||||
run: docker build --build-arg FEATURES=portable -t lighthouse:local .
|
||||
- name: Test the built image
|
||||
run: docker run -t lighthouse:local lighthouse --version
|
||||
basic-simulator-ubuntu:
|
||||
name: basic-simulator-ubuntu
|
||||
needs: [check-labels]
|
||||
@@ -298,41 +287,6 @@ jobs:
|
||||
cache-target: release
|
||||
- name: Run a beacon chain sim which tests VC fallback behaviour
|
||||
run: cargo run --release --bin simulator fallback-sim
|
||||
doppelganger-protection-test:
|
||||
name: doppelganger-protection-test
|
||||
needs: [check-labels]
|
||||
if: needs.check-labels.outputs.skip_ci != 'true'
|
||||
runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest' }}
|
||||
env:
|
||||
# Enable portable to prevent issues with caching `blst` for the wrong CPU type
|
||||
FEATURES: jemalloc,portable
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Get latest version of stable Rust
|
||||
if: env.SELF_HOSTED_RUNNERS == 'false'
|
||||
uses: moonrepo/setup-rust@v1
|
||||
with:
|
||||
channel: stable
|
||||
cache-target: release
|
||||
- name: Install geth
|
||||
if: env.SELF_HOSTED_RUNNERS == 'false'
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:ethereum/ethereum
|
||||
sudo apt-get update
|
||||
sudo apt-get install ethereum
|
||||
- name: Install lighthouse
|
||||
run: |
|
||||
make
|
||||
- name: Install lcli
|
||||
run: make install-lcli
|
||||
- name: Run the doppelganger protection failure test script
|
||||
run: |
|
||||
cd scripts/tests
|
||||
./doppelganger_protection.sh failure genesis.json
|
||||
- name: Run the doppelganger protection success test script
|
||||
run: |
|
||||
cd scripts/tests
|
||||
./doppelganger_protection.sh success genesis.json
|
||||
execution-engine-integration-ubuntu:
|
||||
name: execution-engine-integration-ubuntu
|
||||
needs: [check-labels]
|
||||
@@ -465,10 +419,8 @@ jobs:
|
||||
'debug-tests-ubuntu',
|
||||
'state-transition-vectors-ubuntu',
|
||||
'ef-tests-ubuntu',
|
||||
'dockerfile-ubuntu',
|
||||
'basic-simulator-ubuntu',
|
||||
'fallback-simulator-ubuntu',
|
||||
'doppelganger-protection-test',
|
||||
'execution-engine-integration-ubuntu',
|
||||
'check-code',
|
||||
'check-msrv',
|
||||
|
||||
755
Cargo.lock
generated
755
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,7 @@ clap = { version = "4.5.4", features = ["cargo", "wrap_help"] }
|
||||
# feature ourselves when desired.
|
||||
c-kzg = { version = "1", default-features = false }
|
||||
compare_fields_derive = { path = "common/compare_fields_derive" }
|
||||
criterion = "0.3"
|
||||
criterion = "0.5"
|
||||
delay_map = "0.3"
|
||||
derivative = "2"
|
||||
dirs = "3"
|
||||
@@ -168,7 +168,7 @@ sysinfo = "0.26"
|
||||
tempfile = "3"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "sync", "signal"] }
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
tokio-util = { version = "0.6", features = ["codec", "compat", "time"] }
|
||||
tokio-util = { version = "0.7", features = ["codec", "compat", "time"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-appender = "0.2"
|
||||
tracing-core = "0.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.75.0-bullseye AS builder
|
||||
FROM rust:1.78.0-bullseye AS builder
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev
|
||||
COPY . lighthouse
|
||||
ARG FEATURES
|
||||
|
||||
@@ -18,7 +18,7 @@ pub const WALLETS_DIR_FLAG: &str = "wallets-dir";
|
||||
|
||||
pub fn cli_app() -> Command {
|
||||
Command::new(CMD)
|
||||
.visible_aliases(["a", "am", "account", CMD])
|
||||
.visible_aliases(["a", "am", "account"])
|
||||
.about("Utilities for generating and managing Ethereum 2.0 accounts.")
|
||||
.display_order(0)
|
||||
.arg(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "beacon_node"
|
||||
version = "5.1.3"
|
||||
version = "5.2.0"
|
||||
authors = [
|
||||
"Paul Hauner <paul@paulhauner.com>",
|
||||
"Age Manning <Age@AgeManning.com",
|
||||
|
||||
@@ -427,6 +427,11 @@ fn process_slash_info<T: BeaconChainTypes>(
|
||||
if let Some(slasher) = chain.slasher.as_ref() {
|
||||
let (indexed_attestation, check_signature, err) = match slash_info {
|
||||
SignatureNotChecked(attestation, err) => {
|
||||
if let Error::UnknownHeadBlock { .. } = err {
|
||||
if attestation.data.beacon_block_root == attestation.data.target.root {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
match obtain_indexed_attestation_and_committees_per_slot(chain, attestation) {
|
||||
Ok((indexed, _)) => (indexed, true, err),
|
||||
Err(e) => {
|
||||
|
||||
@@ -3400,6 +3400,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"payload_verification_handle",
|
||||
)
|
||||
.await??;
|
||||
|
||||
// Remove block components from da_checker AFTER completing block import. Then we can assert
|
||||
// the following invariant:
|
||||
// > A valid unfinalized block is either in fork-choice or da_checker.
|
||||
//
|
||||
// If we remove the block when it becomes available, there's some time window during
|
||||
// `import_block` where the block is nowhere. Consumers of the da_checker can handle the
|
||||
// extend time a block may exist in the da_checker.
|
||||
//
|
||||
// If `import_block` errors (only errors with internal errors), the pending components will
|
||||
// be pruned on data_availability_checker maintenance as finality advances.
|
||||
self.data_availability_checker
|
||||
.remove_pending_components(block_root);
|
||||
|
||||
Ok(AvailabilityProcessingStatus::Imported(block_root))
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,11 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
.put_pending_executed_block(executed_block)
|
||||
}
|
||||
|
||||
pub fn remove_pending_components(&self, block_root: Hash256) {
|
||||
self.availability_cache
|
||||
.remove_pending_components(block_root)
|
||||
}
|
||||
|
||||
/// Verifies kzg commitments for an RpcBlock, returns a `MaybeAvailableBlock` that may
|
||||
/// include the fully available block.
|
||||
///
|
||||
|
||||
@@ -498,6 +498,21 @@ impl<T: BeaconChainTypes> Critical<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes and returns the pending_components corresponding to
|
||||
/// the `block_root` or `None` if it does not exist
|
||||
pub fn remove_pending_components(&mut self, block_root: Hash256) {
|
||||
match self.in_memory.pop_entry(&block_root) {
|
||||
Some { .. } => {}
|
||||
None => {
|
||||
// not in memory, is it in the store?
|
||||
// We don't need to remove the data from the store as we have removed it from
|
||||
// `store_keys` so we won't go looking for it on disk. The maintenance thread
|
||||
// will remove it from disk the next time it runs.
|
||||
self.store_keys.remove(&block_root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of pending component entries in memory.
|
||||
pub fn num_blocks(&self) -> usize {
|
||||
self.in_memory.len()
|
||||
@@ -607,6 +622,11 @@ impl<T: BeaconChainTypes> OverflowLRUCache<T> {
|
||||
pending_components.merge_blobs(fixed_blobs);
|
||||
|
||||
if pending_components.is_available() {
|
||||
write_lock.put_pending_components(
|
||||
block_root,
|
||||
pending_components.clone(),
|
||||
&self.overflow_store,
|
||||
)?;
|
||||
// No need to hold the write lock anymore
|
||||
drop(write_lock);
|
||||
pending_components.make_available(|diet_block| {
|
||||
@@ -646,6 +666,11 @@ impl<T: BeaconChainTypes> OverflowLRUCache<T> {
|
||||
|
||||
// Check if we have all components and entire set is consistent.
|
||||
if pending_components.is_available() {
|
||||
write_lock.put_pending_components(
|
||||
block_root,
|
||||
pending_components.clone(),
|
||||
&self.overflow_store,
|
||||
)?;
|
||||
// No need to hold the write lock anymore
|
||||
drop(write_lock);
|
||||
pending_components.make_available(|diet_block| {
|
||||
@@ -661,6 +686,10 @@ impl<T: BeaconChainTypes> OverflowLRUCache<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_pending_components(&self, block_root: Hash256) {
|
||||
self.critical.write().remove_pending_components(block_root);
|
||||
}
|
||||
|
||||
/// write all in memory objects to disk
|
||||
pub fn write_all_to_disk(&self) -> Result<(), AvailabilityCheckError> {
|
||||
let maintenance_lock = self.maintenance_lock.lock();
|
||||
@@ -1195,10 +1224,17 @@ mod test {
|
||||
matches!(availability, Availability::Available(_)),
|
||||
"block doesn't have blobs, should be available"
|
||||
);
|
||||
assert_eq!(
|
||||
cache.critical.read().in_memory.len(),
|
||||
1,
|
||||
"cache should still have block as it hasn't been imported yet"
|
||||
);
|
||||
// remove the blob to simulate successful import
|
||||
cache.remove_pending_components(root);
|
||||
assert_eq!(
|
||||
cache.critical.read().in_memory.len(),
|
||||
0,
|
||||
"cache should be empty because we don't have blobs"
|
||||
"cache should be empty now that block has been imported"
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
@@ -1263,6 +1299,12 @@ mod test {
|
||||
"block should be available: {:?}",
|
||||
availability
|
||||
);
|
||||
assert!(
|
||||
cache.critical.read().in_memory.len() == 1,
|
||||
"cache should still have available block until import"
|
||||
);
|
||||
// remove the blob to simulate successful import
|
||||
cache.remove_pending_components(root);
|
||||
assert!(
|
||||
cache.critical.read().in_memory.is_empty(),
|
||||
"cache should be empty now that all components available"
|
||||
@@ -1378,6 +1420,8 @@ mod test {
|
||||
.expect("should put blob");
|
||||
if blob_index == expected_blobs - 1 {
|
||||
assert!(matches!(availability, Availability::Available(_)));
|
||||
// remove the block from the cache to simulate import
|
||||
cache.remove_pending_components(roots[0]);
|
||||
} else {
|
||||
// the first block should be brought back into memory
|
||||
assert!(
|
||||
|
||||
@@ -60,7 +60,9 @@ use std::time::Duration;
|
||||
use task_executor::TaskExecutor;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::error::TrySendError;
|
||||
use types::{Attestation, Hash256, SignedAggregateAndProof, SubnetId};
|
||||
use types::{
|
||||
Attestation, BeaconState, ChainSpec, Hash256, RelativeEpoch, SignedAggregateAndProof, SubnetId,
|
||||
};
|
||||
use types::{EthSpec, Slot};
|
||||
use work_reprocessing_queue::IgnoredRpcBlock;
|
||||
use work_reprocessing_queue::{
|
||||
@@ -85,123 +87,98 @@ const MAX_IDLE_QUEUE_LEN: usize = 16_384;
|
||||
/// The maximum size of the channel for re-processing work events.
|
||||
const DEFAULT_MAX_SCHEDULED_WORK_QUEUE_LEN: usize = 3 * DEFAULT_MAX_WORK_EVENT_QUEUE_LEN / 4;
|
||||
|
||||
/// The maximum number of queued `Attestation` objects that will be stored before we start dropping
|
||||
/// them.
|
||||
const MAX_UNAGGREGATED_ATTESTATION_QUEUE_LEN: usize = 16_384;
|
||||
/// Over-provision queues based on active validator count by some factor. The beacon chain has
|
||||
/// strict churns that prevent the validator set size from changing rapidly. By over-provisioning
|
||||
/// slightly, we don't need to adjust the queues during the lifetime of a process.
|
||||
const ACTIVE_VALIDATOR_COUNT_OVERPROVISION_PERCENT: usize = 110;
|
||||
|
||||
/// The maximum number of queued `Attestation` objects that will be stored before we start dropping
|
||||
/// them.
|
||||
const MAX_UNAGGREGATED_ATTESTATION_REPROCESS_QUEUE_LEN: usize = 8_192;
|
||||
/// Maximum number of queued items that will be stored before dropping them
|
||||
pub struct BeaconProcessorQueueLengths {
|
||||
aggregate_queue: usize,
|
||||
attestation_queue: usize,
|
||||
unknown_block_aggregate_queue: usize,
|
||||
unknown_block_attestation_queue: usize,
|
||||
sync_message_queue: usize,
|
||||
sync_contribution_queue: usize,
|
||||
gossip_voluntary_exit_queue: usize,
|
||||
gossip_proposer_slashing_queue: usize,
|
||||
gossip_attester_slashing_queue: usize,
|
||||
finality_update_queue: usize,
|
||||
optimistic_update_queue: usize,
|
||||
unknown_light_client_update_queue: usize,
|
||||
rpc_block_queue: usize,
|
||||
rpc_blob_queue: usize,
|
||||
chain_segment_queue: usize,
|
||||
backfill_chain_segment: usize,
|
||||
gossip_block_queue: usize,
|
||||
gossip_blob_queue: usize,
|
||||
delayed_block_queue: usize,
|
||||
status_queue: usize,
|
||||
bbrange_queue: usize,
|
||||
bbroots_queue: usize,
|
||||
blbroots_queue: usize,
|
||||
blbrange_queue: usize,
|
||||
gossip_bls_to_execution_change_queue: usize,
|
||||
lc_bootstrap_queue: usize,
|
||||
lc_optimistic_update_queue: usize,
|
||||
lc_finality_update_queue: usize,
|
||||
api_request_p0_queue: usize,
|
||||
api_request_p1_queue: usize,
|
||||
}
|
||||
|
||||
/// The maximum number of queued `SignedAggregateAndProof` objects that will be stored before we
|
||||
/// start dropping them.
|
||||
const MAX_AGGREGATED_ATTESTATION_QUEUE_LEN: usize = 4_096;
|
||||
impl BeaconProcessorQueueLengths {
|
||||
pub fn from_state<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, String> {
|
||||
let active_validator_count =
|
||||
match state.get_cached_active_validator_indices(RelativeEpoch::Current) {
|
||||
Ok(indices) => indices.len(),
|
||||
Err(_) => state
|
||||
.get_active_validator_indices(state.current_epoch(), spec)
|
||||
.map_err(|e| format!("Error computing active indices: {:?}", e))?
|
||||
.len(),
|
||||
};
|
||||
let active_validator_count =
|
||||
(ACTIVE_VALIDATOR_COUNT_OVERPROVISION_PERCENT * active_validator_count) / 100;
|
||||
let slots_per_epoch = E::slots_per_epoch() as usize;
|
||||
|
||||
/// The maximum number of queued `SignedAggregateAndProof` objects that will be stored before we
|
||||
/// start dropping them.
|
||||
const MAX_AGGREGATED_ATTESTATION_REPROCESS_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `SignedBeaconBlock` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_BLOCK_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `BlobSidecar` objects received on gossip that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_GOSSIP_BLOB_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `SignedBeaconBlock` objects received prior to their slot (but
|
||||
/// within acceptable clock disparity) that will be queued before we start dropping them.
|
||||
const MAX_DELAYED_BLOCK_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `SignedVoluntaryExit` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_EXIT_QUEUE_LEN: usize = 4_096;
|
||||
|
||||
/// The maximum number of queued `ProposerSlashing` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_PROPOSER_SLASHING_QUEUE_LEN: usize = 4_096;
|
||||
|
||||
/// The maximum number of queued `AttesterSlashing` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_ATTESTER_SLASHING_QUEUE_LEN: usize = 4_096;
|
||||
|
||||
/// The maximum number of queued `LightClientFinalityUpdate` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_FINALITY_UPDATE_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `LightClientOptimisticUpdate` objects received on gossip that will be stored
|
||||
/// before we start dropping them.
|
||||
const MAX_GOSSIP_OPTIMISTIC_UPDATE_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `LightClientOptimisticUpdate` objects received on gossip that will be stored
|
||||
/// for reprocessing before we start dropping them.
|
||||
const MAX_GOSSIP_OPTIMISTIC_UPDATE_REPROCESS_QUEUE_LEN: usize = 128;
|
||||
|
||||
/// The maximum number of queued `SyncCommitteeMessage` objects that will be stored before we start dropping
|
||||
/// them.
|
||||
const MAX_SYNC_MESSAGE_QUEUE_LEN: usize = 2048;
|
||||
|
||||
/// The maximum number of queued `SignedContributionAndProof` objects that will be stored before we
|
||||
/// start dropping them.
|
||||
const MAX_SYNC_CONTRIBUTION_QUEUE_LEN: usize = 1024;
|
||||
|
||||
/// The maximum number of queued `SignedBeaconBlock` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_RPC_BLOCK_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `BlobSidecar` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_RPC_BLOB_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `Vec<SignedBeaconBlock>` objects received during syncing that will
|
||||
/// be stored before we start dropping them.
|
||||
const MAX_CHAIN_SEGMENT_QUEUE_LEN: usize = 64;
|
||||
|
||||
/// The maximum number of queued `StatusMessage` objects received from the network RPC that will be
|
||||
/// stored before we start dropping them.
|
||||
const MAX_STATUS_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `BlocksByRangeRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_BLOCKS_BY_RANGE_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `BlobsByRangeRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_BLOBS_BY_RANGE_QUEUE_LEN: usize = 1024;
|
||||
|
||||
/// The maximum number of queued `BlocksByRootRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_BLOCKS_BY_ROOTS_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `BlobsByRootRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_BLOBS_BY_ROOTS_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// Maximum number of `SignedBlsToExecutionChange` messages to queue before dropping them.
|
||||
///
|
||||
/// This value is set high to accommodate the large spike that is expected immediately after Capella
|
||||
/// is activated.
|
||||
const MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN: usize = 16_384;
|
||||
|
||||
/// The maximum number of queued `LightClientBootstrapRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of queued `LightClientOptimisticUpdateRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUEUE_LEN: usize = 512;
|
||||
|
||||
/// The maximum number of queued `LightClientFinalityUpdateRequest` objects received from the network RPC that
|
||||
/// will be stored before we start dropping them.
|
||||
const MAX_LIGHT_CLIENT_FINALITY_UPDATE_QUEUE_LEN: usize = 512;
|
||||
|
||||
/// The maximum number of priority-0 (highest priority) messages that will be queued before
|
||||
/// they begin to be dropped.
|
||||
const MAX_API_REQUEST_P0_QUEUE_LEN: usize = 1_024;
|
||||
|
||||
/// The maximum number of priority-1 (second-highest priority) messages that will be queued before
|
||||
/// they begin to be dropped.
|
||||
const MAX_API_REQUEST_P1_QUEUE_LEN: usize = 1_024;
|
||||
Ok(Self {
|
||||
aggregate_queue: 4096,
|
||||
unknown_block_aggregate_queue: 1024,
|
||||
// Capacity for a full slot's worth of attestations if subscribed to all subnets
|
||||
attestation_queue: active_validator_count / slots_per_epoch,
|
||||
// Capacity for a full slot's worth of attestations if subscribed to all subnets
|
||||
unknown_block_attestation_queue: active_validator_count / slots_per_epoch,
|
||||
sync_message_queue: 2048,
|
||||
sync_contribution_queue: 1024,
|
||||
gossip_voluntary_exit_queue: 4096,
|
||||
gossip_proposer_slashing_queue: 4096,
|
||||
gossip_attester_slashing_queue: 4096,
|
||||
finality_update_queue: 1024,
|
||||
optimistic_update_queue: 1024,
|
||||
unknown_light_client_update_queue: 128,
|
||||
rpc_block_queue: 1024,
|
||||
rpc_blob_queue: 1024,
|
||||
chain_segment_queue: 64,
|
||||
backfill_chain_segment: 64,
|
||||
gossip_block_queue: 1024,
|
||||
gossip_blob_queue: 1024,
|
||||
delayed_block_queue: 1024,
|
||||
status_queue: 1024,
|
||||
bbrange_queue: 1024,
|
||||
bbroots_queue: 1024,
|
||||
blbroots_queue: 1024,
|
||||
blbrange_queue: 1024,
|
||||
gossip_bls_to_execution_change_queue: 16384,
|
||||
lc_bootstrap_queue: 1024,
|
||||
lc_optimistic_update_queue: 512,
|
||||
lc_finality_update_queue: 512,
|
||||
api_request_p0_queue: 1024,
|
||||
api_request_p1_queue: 1024,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of the manager tokio task.
|
||||
const MANAGER_TASK_NAME: &str = "beacon_processor_manager";
|
||||
@@ -772,6 +749,7 @@ impl<E: EthSpec> BeaconProcessor<E> {
|
||||
///
|
||||
/// The optional `work_journal_tx` allows for an outside process to receive a log of all work
|
||||
/// events processed by `self`. This should only be used during testing.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn spawn_manager<S: SlotClock + 'static>(
|
||||
mut self,
|
||||
event_rx: mpsc::Receiver<WorkEvent<E>>,
|
||||
@@ -780,6 +758,7 @@ impl<E: EthSpec> BeaconProcessor<E> {
|
||||
work_journal_tx: Option<mpsc::Sender<&'static str>>,
|
||||
slot_clock: S,
|
||||
maximum_gossip_clock_disparity: Duration,
|
||||
queue_lengths: BeaconProcessorQueueLengths,
|
||||
) -> Result<(), String> {
|
||||
// Used by workers to communicate that they are finished a task.
|
||||
let (idle_tx, idle_rx) = mpsc::channel::<()>(MAX_IDLE_QUEUE_LEN);
|
||||
@@ -787,61 +766,61 @@ impl<E: EthSpec> BeaconProcessor<E> {
|
||||
// Using LIFO queues for attestations since validator profits rely upon getting fresh
|
||||
// attestations into blocks. Additionally, later attestations contain more information than
|
||||
// earlier ones, so we consider them more valuable.
|
||||
let mut aggregate_queue = LifoQueue::new(MAX_AGGREGATED_ATTESTATION_QUEUE_LEN);
|
||||
let mut aggregate_queue = LifoQueue::new(queue_lengths.aggregate_queue);
|
||||
let mut aggregate_debounce = TimeLatch::default();
|
||||
let mut attestation_queue = LifoQueue::new(MAX_UNAGGREGATED_ATTESTATION_QUEUE_LEN);
|
||||
let mut attestation_queue = LifoQueue::new(queue_lengths.attestation_queue);
|
||||
let mut attestation_debounce = TimeLatch::default();
|
||||
let mut unknown_block_aggregate_queue =
|
||||
LifoQueue::new(MAX_AGGREGATED_ATTESTATION_REPROCESS_QUEUE_LEN);
|
||||
LifoQueue::new(queue_lengths.unknown_block_aggregate_queue);
|
||||
let mut unknown_block_attestation_queue =
|
||||
LifoQueue::new(MAX_UNAGGREGATED_ATTESTATION_REPROCESS_QUEUE_LEN);
|
||||
LifoQueue::new(queue_lengths.unknown_block_attestation_queue);
|
||||
|
||||
let mut sync_message_queue = LifoQueue::new(MAX_SYNC_MESSAGE_QUEUE_LEN);
|
||||
let mut sync_contribution_queue = LifoQueue::new(MAX_SYNC_CONTRIBUTION_QUEUE_LEN);
|
||||
let mut sync_message_queue = LifoQueue::new(queue_lengths.sync_message_queue);
|
||||
let mut sync_contribution_queue = LifoQueue::new(queue_lengths.sync_contribution_queue);
|
||||
|
||||
// Using a FIFO queue for voluntary exits since it prevents exit censoring. I don't have
|
||||
// a strong feeling about queue type for exits.
|
||||
let mut gossip_voluntary_exit_queue = FifoQueue::new(MAX_GOSSIP_EXIT_QUEUE_LEN);
|
||||
let mut gossip_voluntary_exit_queue =
|
||||
FifoQueue::new(queue_lengths.gossip_voluntary_exit_queue);
|
||||
|
||||
// Using a FIFO queue for slashing to prevent people from flushing their slashings from the
|
||||
// queues with lots of junk messages.
|
||||
let mut gossip_proposer_slashing_queue =
|
||||
FifoQueue::new(MAX_GOSSIP_PROPOSER_SLASHING_QUEUE_LEN);
|
||||
FifoQueue::new(queue_lengths.gossip_proposer_slashing_queue);
|
||||
let mut gossip_attester_slashing_queue =
|
||||
FifoQueue::new(MAX_GOSSIP_ATTESTER_SLASHING_QUEUE_LEN);
|
||||
FifoQueue::new(queue_lengths.gossip_attester_slashing_queue);
|
||||
|
||||
// Using a FIFO queue for light client updates to maintain sequence order.
|
||||
let mut finality_update_queue = FifoQueue::new(MAX_GOSSIP_FINALITY_UPDATE_QUEUE_LEN);
|
||||
let mut optimistic_update_queue = FifoQueue::new(MAX_GOSSIP_OPTIMISTIC_UPDATE_QUEUE_LEN);
|
||||
let mut finality_update_queue = FifoQueue::new(queue_lengths.finality_update_queue);
|
||||
let mut optimistic_update_queue = FifoQueue::new(queue_lengths.optimistic_update_queue);
|
||||
let mut unknown_light_client_update_queue =
|
||||
FifoQueue::new(MAX_GOSSIP_OPTIMISTIC_UPDATE_REPROCESS_QUEUE_LEN);
|
||||
FifoQueue::new(queue_lengths.unknown_light_client_update_queue);
|
||||
|
||||
// Using a FIFO queue since blocks need to be imported sequentially.
|
||||
let mut rpc_block_queue = FifoQueue::new(MAX_RPC_BLOCK_QUEUE_LEN);
|
||||
let mut rpc_blob_queue = FifoQueue::new(MAX_RPC_BLOB_QUEUE_LEN);
|
||||
let mut chain_segment_queue = FifoQueue::new(MAX_CHAIN_SEGMENT_QUEUE_LEN);
|
||||
let mut backfill_chain_segment = FifoQueue::new(MAX_CHAIN_SEGMENT_QUEUE_LEN);
|
||||
let mut gossip_block_queue = FifoQueue::new(MAX_GOSSIP_BLOCK_QUEUE_LEN);
|
||||
let mut gossip_blob_queue = FifoQueue::new(MAX_GOSSIP_BLOB_QUEUE_LEN);
|
||||
let mut delayed_block_queue = FifoQueue::new(MAX_DELAYED_BLOCK_QUEUE_LEN);
|
||||
let mut rpc_block_queue = FifoQueue::new(queue_lengths.rpc_block_queue);
|
||||
let mut rpc_blob_queue = FifoQueue::new(queue_lengths.rpc_blob_queue);
|
||||
let mut chain_segment_queue = FifoQueue::new(queue_lengths.chain_segment_queue);
|
||||
let mut backfill_chain_segment = FifoQueue::new(queue_lengths.backfill_chain_segment);
|
||||
let mut gossip_block_queue = FifoQueue::new(queue_lengths.gossip_block_queue);
|
||||
let mut gossip_blob_queue = FifoQueue::new(queue_lengths.gossip_blob_queue);
|
||||
let mut delayed_block_queue = FifoQueue::new(queue_lengths.delayed_block_queue);
|
||||
|
||||
let mut status_queue = FifoQueue::new(MAX_STATUS_QUEUE_LEN);
|
||||
let mut bbrange_queue = FifoQueue::new(MAX_BLOCKS_BY_RANGE_QUEUE_LEN);
|
||||
let mut bbroots_queue = FifoQueue::new(MAX_BLOCKS_BY_ROOTS_QUEUE_LEN);
|
||||
let mut blbroots_queue = FifoQueue::new(MAX_BLOBS_BY_ROOTS_QUEUE_LEN);
|
||||
let mut blbrange_queue = FifoQueue::new(MAX_BLOBS_BY_RANGE_QUEUE_LEN);
|
||||
let mut status_queue = FifoQueue::new(queue_lengths.status_queue);
|
||||
let mut bbrange_queue = FifoQueue::new(queue_lengths.bbrange_queue);
|
||||
let mut bbroots_queue = FifoQueue::new(queue_lengths.bbroots_queue);
|
||||
let mut blbroots_queue = FifoQueue::new(queue_lengths.blbroots_queue);
|
||||
let mut blbrange_queue = FifoQueue::new(queue_lengths.blbrange_queue);
|
||||
|
||||
let mut gossip_bls_to_execution_change_queue =
|
||||
FifoQueue::new(MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN);
|
||||
FifoQueue::new(queue_lengths.gossip_bls_to_execution_change_queue);
|
||||
|
||||
let mut lc_bootstrap_queue = FifoQueue::new(MAX_LIGHT_CLIENT_BOOTSTRAP_QUEUE_LEN);
|
||||
let mut lc_bootstrap_queue = FifoQueue::new(queue_lengths.lc_bootstrap_queue);
|
||||
let mut lc_optimistic_update_queue =
|
||||
FifoQueue::new(MAX_LIGHT_CLIENT_OPTIMISTIC_UPDATE_QUEUE_LEN);
|
||||
let mut lc_finality_update_queue =
|
||||
FifoQueue::new(MAX_LIGHT_CLIENT_FINALITY_UPDATE_QUEUE_LEN);
|
||||
FifoQueue::new(queue_lengths.lc_optimistic_update_queue);
|
||||
let mut lc_finality_update_queue = FifoQueue::new(queue_lengths.lc_finality_update_queue);
|
||||
|
||||
let mut api_request_p0_queue = FifoQueue::new(MAX_API_REQUEST_P0_QUEUE_LEN);
|
||||
let mut api_request_p1_queue = FifoQueue::new(MAX_API_REQUEST_P1_QUEUE_LEN);
|
||||
let mut api_request_p0_queue = FifoQueue::new(queue_lengths.api_request_p0_queue);
|
||||
let mut api_request_p1_queue = FifoQueue::new(queue_lengths.api_request_p1_queue);
|
||||
|
||||
// Channels for sending work to the re-process scheduler (`work_reprocessing_tx`) and to
|
||||
// receive them back once they are ready (`ready_work_rx`).
|
||||
|
||||
@@ -28,7 +28,6 @@ use std::time::Duration;
|
||||
use strum::AsRefStr;
|
||||
use task_executor::TaskExecutor;
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
use tokio::time::error::Error as TimeError;
|
||||
use tokio_util::time::delay_queue::{DelayQueue, Key as DelayKey};
|
||||
use types::{EthSpec, Hash256, Slot};
|
||||
|
||||
@@ -196,8 +195,6 @@ enum InboundEvent {
|
||||
ReadyLightClientUpdate(QueuedLightClientUpdateId),
|
||||
/// A backfill batch that was queued is ready for processing.
|
||||
ReadyBackfillSync(QueuedBackfillBatch),
|
||||
/// A `DelayQueue` returned an error.
|
||||
DelayQueueError(TimeError, &'static str),
|
||||
/// A message sent to the `ReprocessQueue`
|
||||
Msg(ReprocessQueueMessage),
|
||||
}
|
||||
@@ -279,54 +276,42 @@ impl<S: SlotClock> Stream for ReprocessQueue<S> {
|
||||
// The sequential nature of blockchains means it is generally better to try and import all
|
||||
// existing blocks before new ones.
|
||||
match self.gossip_block_delay_queue.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(queued_block))) => {
|
||||
Poll::Ready(Some(queued_block)) => {
|
||||
return Poll::Ready(Some(InboundEvent::ReadyGossipBlock(
|
||||
queued_block.into_inner(),
|
||||
)));
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
return Poll::Ready(Some(InboundEvent::DelayQueueError(e, "gossip_block_queue")));
|
||||
}
|
||||
// `Poll::Ready(None)` means that there are no more entries in the delay queue and we
|
||||
// will continue to get this result until something else is added into the queue.
|
||||
Poll::Ready(None) | Poll::Pending => (),
|
||||
}
|
||||
|
||||
match self.rpc_block_delay_queue.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(queued_block))) => {
|
||||
Poll::Ready(Some(queued_block)) => {
|
||||
return Poll::Ready(Some(InboundEvent::ReadyRpcBlock(queued_block.into_inner())));
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
return Poll::Ready(Some(InboundEvent::DelayQueueError(e, "rpc_block_queue")));
|
||||
}
|
||||
// `Poll::Ready(None)` means that there are no more entries in the delay queue and we
|
||||
// will continue to get this result until something else is added into the queue.
|
||||
Poll::Ready(None) | Poll::Pending => (),
|
||||
}
|
||||
|
||||
match self.attestations_delay_queue.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(attestation_id))) => {
|
||||
Poll::Ready(Some(attestation_id)) => {
|
||||
return Poll::Ready(Some(InboundEvent::ReadyAttestation(
|
||||
attestation_id.into_inner(),
|
||||
)));
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
return Poll::Ready(Some(InboundEvent::DelayQueueError(e, "attestations_queue")));
|
||||
}
|
||||
// `Poll::Ready(None)` means that there are no more entries in the delay queue and we
|
||||
// will continue to get this result until something else is added into the queue.
|
||||
Poll::Ready(None) | Poll::Pending => (),
|
||||
}
|
||||
|
||||
match self.lc_updates_delay_queue.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(lc_id))) => {
|
||||
Poll::Ready(Some(lc_id)) => {
|
||||
return Poll::Ready(Some(InboundEvent::ReadyLightClientUpdate(
|
||||
lc_id.into_inner(),
|
||||
)));
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
return Poll::Ready(Some(InboundEvent::DelayQueueError(e, "lc_updates_queue")));
|
||||
}
|
||||
// `Poll::Ready(None)` means that there are no more entries in the delay queue and we
|
||||
// will continue to get this result until something else is added into the queue.
|
||||
Poll::Ready(None) | Poll::Pending => (),
|
||||
@@ -786,14 +771,6 @@ impl<S: SlotClock> ReprocessQueue<S> {
|
||||
);
|
||||
}
|
||||
}
|
||||
InboundEvent::DelayQueueError(e, queue_name) => {
|
||||
crit!(
|
||||
log,
|
||||
"Failed to poll queue";
|
||||
"queue" => queue_name,
|
||||
"e" => ?e
|
||||
)
|
||||
}
|
||||
InboundEvent::ReadyAttestation(queued_id) => {
|
||||
metrics::inc_counter(
|
||||
&metrics::BEACON_PROCESSOR_REPROCESSING_QUEUE_EXPIRED_ATTESTATIONS,
|
||||
|
||||
@@ -19,8 +19,8 @@ use beacon_chain::{
|
||||
store::{HotColdDB, ItemStore, LevelDB, StoreConfig},
|
||||
BeaconChain, BeaconChainTypes, Eth1ChainBackend, MigratorConfig, ServerSentEventHandler,
|
||||
};
|
||||
use beacon_processor::BeaconProcessorConfig;
|
||||
use beacon_processor::{BeaconProcessor, BeaconProcessorChannels};
|
||||
use beacon_processor::{BeaconProcessorConfig, BeaconProcessorQueueLengths};
|
||||
use environment::RuntimeContext;
|
||||
use eth1::{Config as Eth1Config, Service as Eth1Service};
|
||||
use eth2::{
|
||||
@@ -884,6 +884,14 @@ where
|
||||
None,
|
||||
beacon_chain.slot_clock.clone(),
|
||||
beacon_chain.spec.maximum_gossip_clock_disparity(),
|
||||
BeaconProcessorQueueLengths::from_state(
|
||||
&beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state,
|
||||
&beacon_chain.spec,
|
||||
)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -805,6 +805,9 @@ pub mod serde_logs_bloom {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JsonClientVersionV1 {
|
||||
pub code: String,
|
||||
// This `default` is required until Geth v1.13.x is no longer supported on mainnet.
|
||||
// See: https://github.com/ethereum/go-ethereum/pull/29351
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub commit: String,
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
//! NOTE: These tests will not pass unless an anvil is running on `ENDPOINT` (see below).
|
||||
//!
|
||||
//! You can start a suitable instance using the `anvil_test_node.sh` script in the `scripts`
|
||||
//! dir in the root of the `lighthouse` repo.
|
||||
#![cfg(test)]
|
||||
use environment::{Environment, EnvironmentBuilder};
|
||||
use eth1::{Eth1Endpoint, DEFAULT_CHAIN_ID};
|
||||
|
||||
@@ -3,7 +3,9 @@ use beacon_chain::{
|
||||
test_utils::{BeaconChainHarness, BoxedMutator, Builder, EphemeralHarnessType},
|
||||
BeaconChain, BeaconChainTypes,
|
||||
};
|
||||
use beacon_processor::{BeaconProcessor, BeaconProcessorChannels, BeaconProcessorConfig};
|
||||
use beacon_processor::{
|
||||
BeaconProcessor, BeaconProcessorChannels, BeaconProcessorConfig, BeaconProcessorQueueLengths,
|
||||
};
|
||||
use directory::DEFAULT_ROOT_DIR;
|
||||
use eth2::{BeaconNodeHttpClient, Timeouts};
|
||||
use lighthouse_network::{
|
||||
@@ -206,6 +208,11 @@ pub async fn create_api_server<T: BeaconChainTypes>(
|
||||
None,
|
||||
chain.slot_clock.clone(),
|
||||
chain.spec.maximum_gossip_clock_disparity(),
|
||||
BeaconProcessorQueueLengths::from_state(
|
||||
&chain.canonical_head.cached_head().snapshot.beacon_state,
|
||||
&chain.spec,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = { workspace = true }
|
||||
[dependencies]
|
||||
discv5 = { workspace = true }
|
||||
gossipsub = { workspace = true }
|
||||
unsigned-varint = { version = "0.6", features = ["codec"] }
|
||||
unsigned-varint = { version = "0.8", features = ["codec"] }
|
||||
ssz_types = { workspace = true }
|
||||
types = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -15,7 +15,7 @@ use libp2p::swarm::handler::{
|
||||
FullyNegotiatedInbound, FullyNegotiatedOutbound, StreamUpgradeError, SubstreamProtocol,
|
||||
};
|
||||
use libp2p::swarm::Stream;
|
||||
use slog::{crit, debug, trace, warn};
|
||||
use slog::{crit, debug, trace};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, VecDeque},
|
||||
@@ -414,9 +414,8 @@ where
|
||||
}
|
||||
|
||||
// purge expired inbound substreams and send an error
|
||||
loop {
|
||||
match self.inbound_substreams_delay.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(inbound_id))) => {
|
||||
|
||||
while let Poll::Ready(Some(inbound_id)) = self.inbound_substreams_delay.poll_expired(cx) {
|
||||
// handle a stream timeout for various states
|
||||
if let Some(info) = self.inbound_substreams.get_mut(inbound_id.get_ref()) {
|
||||
// the delay has been removed
|
||||
@@ -436,23 +435,9 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
warn!(self.log, "Inbound substream poll failed"; "error" => ?e);
|
||||
// drops the peer if we cannot read the delay queue
|
||||
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
|
||||
HandlerEvent::Close(RPCError::InternalError(
|
||||
"Could not poll inbound stream timer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
Poll::Pending | Poll::Ready(None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
// purge expired outbound substreams
|
||||
loop {
|
||||
match self.outbound_substreams_delay.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(outbound_id))) => {
|
||||
while let Poll::Ready(Some(outbound_id)) = self.outbound_substreams_delay.poll_expired(cx) {
|
||||
if let Some(OutboundInfo { proto, req_id, .. }) =
|
||||
self.outbound_substreams.remove(outbound_id.get_ref())
|
||||
{
|
||||
@@ -462,24 +447,13 @@ where
|
||||
error: RPCError::StreamTimeout,
|
||||
};
|
||||
// notify the user
|
||||
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
|
||||
HandlerEvent::Err(outbound_err),
|
||||
));
|
||||
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(HandlerEvent::Err(
|
||||
outbound_err,
|
||||
)));
|
||||
} else {
|
||||
crit!(self.log, "timed out substream not in the books"; "stream_id" => outbound_id.get_ref());
|
||||
}
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
warn!(self.log, "Outbound substream poll failed"; "error" => ?e);
|
||||
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
|
||||
HandlerEvent::Close(RPCError::InternalError(
|
||||
"Could not poll outbound stream timer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
Poll::Pending | Poll::Ready(None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
// when deactivated, close all streams
|
||||
let deactivated = matches!(self.state, HandlerState::Deactivated);
|
||||
|
||||
@@ -10,7 +10,7 @@ use libp2p::swarm::{
|
||||
handler::ConnectionHandler, CloseConnection, ConnectionId, NetworkBehaviour, NotifyHandler,
|
||||
ToSwarm,
|
||||
};
|
||||
use libp2p::swarm::{FromSwarm, SubstreamProtocol, THandlerInEvent};
|
||||
use libp2p::swarm::{ConnectionClosed, FromSwarm, SubstreamProtocol, THandlerInEvent};
|
||||
use libp2p::PeerId;
|
||||
use rate_limiter::{RPCRateLimiter as RateLimiter, RateLimitedErr};
|
||||
use slog::{crit, debug, o};
|
||||
@@ -283,9 +283,40 @@ where
|
||||
Ok(handler)
|
||||
}
|
||||
|
||||
fn on_swarm_event(&mut self, _event: FromSwarm) {
|
||||
fn on_swarm_event(&mut self, event: FromSwarm) {
|
||||
// NOTE: FromSwarm is a non exhaustive enum so updates should be based on release notes more
|
||||
// than compiler feedback
|
||||
// The self rate limiter holds on to requests and attempts to process them within our rate
|
||||
// limits. If a peer disconnects whilst we are self-rate limiting, we want to terminate any
|
||||
// pending requests and return an error response to the application.
|
||||
|
||||
if let FromSwarm::ConnectionClosed(ConnectionClosed {
|
||||
peer_id,
|
||||
remaining_established,
|
||||
connection_id,
|
||||
..
|
||||
}) = event
|
||||
{
|
||||
// If there are still connections remaining, do nothing.
|
||||
if remaining_established > 0 {
|
||||
return;
|
||||
}
|
||||
// Get a list of pending requests from the self rate limiter
|
||||
if let Some(limiter) = self.self_limiter.as_mut() {
|
||||
for (id, proto) in limiter.peer_disconnected(peer_id) {
|
||||
let error_msg = ToSwarm::GenerateEvent(RPCMessage {
|
||||
peer_id,
|
||||
conn_id: connection_id,
|
||||
event: HandlerEvent::Err(HandlerErr::Outbound {
|
||||
id,
|
||||
proto,
|
||||
error: RPCError::Disconnected,
|
||||
}),
|
||||
});
|
||||
self.events.push(error_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_connection_handler_event(
|
||||
|
||||
@@ -158,13 +158,39 @@ impl<Id: ReqId, E: EthSpec> SelfRateLimiter<Id, E> {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
// NOTE: There can be entries that have been removed due to peer disconnections, we simply
|
||||
// ignore these messages here.
|
||||
}
|
||||
|
||||
/// Informs the limiter that a peer has disconnected. This removes any pending requests and
|
||||
/// returns their IDs.
|
||||
pub fn peer_disconnected(&mut self, peer_id: PeerId) -> Vec<(Id, Protocol)> {
|
||||
// It's not ideal to iterate this map, but the key is (PeerId, Protocol) and this map
|
||||
// should never really be large. So we iterate for simplicity
|
||||
let mut failed_requests = Vec::new();
|
||||
self.delayed_requests
|
||||
.retain(|(map_peer_id, protocol), queue| {
|
||||
if map_peer_id == &peer_id {
|
||||
// NOTE: Currently cannot remove entries from the DelayQueue, we will just let
|
||||
// them expire and ignore them.
|
||||
for message in queue {
|
||||
failed_requests.push((message.request_id, *protocol))
|
||||
}
|
||||
// Remove the entry
|
||||
false
|
||||
} else {
|
||||
// Keep the entry
|
||||
true
|
||||
}
|
||||
});
|
||||
failed_requests
|
||||
}
|
||||
|
||||
pub fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<BehaviourAction<Id, E>> {
|
||||
// First check the requests that were self rate limited, since those might add events to
|
||||
// the queue. Also do this this before rate limiter prunning to avoid removing and
|
||||
// immediately adding rate limiting keys.
|
||||
if let Poll::Ready(Some(Ok(expired))) = self.next_peer_request.poll_expired(cx) {
|
||||
if let Poll::Ready(Some(expired)) = self.next_peer_request.poll_expired(cx) {
|
||||
let (peer_id, protocol) = expired.into_inner();
|
||||
self.next_peer_request_ready(peer_id, protocol);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ impl futures::stream::Stream for GossipCache {
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
match self.expirations.poll_expired(cx) {
|
||||
Poll::Ready(Some(Ok(expired))) => {
|
||||
Poll::Ready(Some(expired)) => {
|
||||
let expected_key = expired.key();
|
||||
let (topic, data) = expired.into_inner();
|
||||
match self.topic_msgs.get_mut(&topic) {
|
||||
@@ -259,7 +259,6 @@ impl futures::stream::Stream for GossipCache {
|
||||
}
|
||||
Poll::Ready(Some(Ok(topic)))
|
||||
}
|
||||
Poll::Ready(Some(Err(x))) => Poll::Ready(Some(Err(x.to_string()))),
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
|
||||
@@ -917,12 +917,23 @@ impl<AppReqId: ReqId, E: EthSpec> Network<AppReqId, E> {
|
||||
/* Eth2 RPC behaviour functions */
|
||||
|
||||
/// Send a request to a peer over RPC.
|
||||
pub fn send_request(&mut self, peer_id: PeerId, request_id: AppReqId, request: Request) {
|
||||
pub fn send_request(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: AppReqId,
|
||||
request: Request,
|
||||
) -> Result<(), (AppReqId, RPCError)> {
|
||||
// Check if the peer is connected before sending an RPC request
|
||||
if !self.swarm.is_connected(&peer_id) {
|
||||
return Err((request_id, RPCError::Disconnected));
|
||||
}
|
||||
|
||||
self.eth2_rpc_mut().send_request(
|
||||
peer_id,
|
||||
RequestId::Application(request_id),
|
||||
request.into(),
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a successful response to a peer over RPC.
|
||||
@@ -1378,26 +1389,17 @@ impl<AppReqId: ReqId, E: EthSpec> Network<AppReqId, E> {
|
||||
) -> Option<NetworkEvent<AppReqId, E>> {
|
||||
let peer_id = event.peer_id;
|
||||
|
||||
if !self.peer_manager().is_connected(&peer_id) {
|
||||
// Sync expects a RPCError::Disconnected to drop associated lookups with this peer.
|
||||
// Silencing this event breaks the API contract with RPC where every request ends with
|
||||
// - A stream termination event, or
|
||||
// - An RPCError event
|
||||
return if let HandlerEvent::Err(HandlerErr::Outbound {
|
||||
id: RequestId::Application(id),
|
||||
error,
|
||||
..
|
||||
}) = event.event
|
||||
// Do not permit Inbound events from peers that are being disconnected, or RPC requests.
|
||||
if !self.peer_manager().is_connected(&peer_id)
|
||||
&& (matches!(event.event, HandlerEvent::Err(HandlerErr::Inbound { .. }))
|
||||
|| matches!(event.event, HandlerEvent::Ok(RPCReceived::Request(..))))
|
||||
{
|
||||
Some(NetworkEvent::RPCFailed { peer_id, id, error })
|
||||
} else {
|
||||
debug!(
|
||||
self.log,
|
||||
"Ignoring rpc message of disconnecting peer";
|
||||
event
|
||||
);
|
||||
None
|
||||
};
|
||||
return None;
|
||||
}
|
||||
|
||||
let handler_id = event.conn_id;
|
||||
|
||||
@@ -98,7 +98,9 @@ fn test_tcp_status_rpc() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, 10, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, 10, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -202,7 +204,9 @@ fn test_tcp_blocks_by_range_chunked_rpc() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, request_id, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, request_id, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -327,7 +331,9 @@ fn test_blobs_by_range_chunked_rpc() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, request_id, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, request_id, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -435,7 +441,9 @@ fn test_tcp_blocks_by_range_over_limit() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, request_id, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, request_id, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
// The request will fail because the sender will refuse to send anything > MAX_RPC_SIZE
|
||||
NetworkEvent::RPCFailed { id, .. } => {
|
||||
@@ -528,7 +536,9 @@ fn test_tcp_blocks_by_range_chunked_rpc_terminates_correctly() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, request_id, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, request_id, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -657,7 +667,9 @@ fn test_tcp_blocks_by_range_single_empty_rpc() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, 10, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, 10, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -780,7 +792,9 @@ fn test_tcp_blocks_by_root_chunked_rpc() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, 6, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, 6, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -911,7 +925,9 @@ fn test_tcp_blocks_by_root_chunked_rpc_terminates_correctly() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, 10, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, 10, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::ResponseReceived {
|
||||
peer_id: _,
|
||||
@@ -1031,7 +1047,9 @@ fn test_disconnect_triggers_rpc_error() {
|
||||
NetworkEvent::PeerConnectedOutgoing(peer_id) => {
|
||||
// Send a STATUS message
|
||||
debug!(log, "Sending RPC");
|
||||
sender.send_request(peer_id, 42, rpc_request.clone());
|
||||
sender
|
||||
.send_request(peer_id, 42, rpc_request.clone())
|
||||
.unwrap();
|
||||
}
|
||||
NetworkEvent::RPCFailed { error, id: 42, .. } => match error {
|
||||
RPCError::Disconnected => return,
|
||||
|
||||
@@ -239,6 +239,11 @@ impl TestRig {
|
||||
Some(work_journal_tx),
|
||||
harness.chain.slot_clock.clone(),
|
||||
chain.spec.maximum_gossip_clock_disparity(),
|
||||
BeaconProcessorQueueLengths::from_state(
|
||||
&chain.canonical_head.cached_head().snapshot.beacon_state,
|
||||
&chain.spec,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
assert!(beacon_processor.is_ok());
|
||||
|
||||
@@ -613,7 +613,15 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
request,
|
||||
request_id,
|
||||
} => {
|
||||
self.libp2p.send_request(peer_id, request_id, request);
|
||||
if let Err((request_id, error)) =
|
||||
self.libp2p.send_request(peer_id, request_id, request)
|
||||
{
|
||||
self.send_to_router(RouterMessage::RPCFailed {
|
||||
peer_id,
|
||||
request_id,
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
NetworkMessage::SendResponse {
|
||||
peer_id,
|
||||
|
||||
@@ -1101,6 +1101,8 @@ pub fn cli_app() -> Command {
|
||||
[Enabled by default].")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("true")
|
||||
.num_args(0..=1)
|
||||
.default_missing_value("true")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
|
||||
@@ -356,7 +356,7 @@ Options:
|
||||
--slasher-backend <DATABASE>
|
||||
Set the database backend to be used by the slasher. [possible values:
|
||||
lmdb, disabled]
|
||||
--slasher-broadcast <slasher-broadcast>
|
||||
--slasher-broadcast [<slasher-broadcast>]
|
||||
Broadcast slashings found by the slasher to the rest of the network
|
||||
[Enabled by default]. [default: true]
|
||||
--slasher-chunk-size <EPOCHS>
|
||||
|
||||
@@ -9,7 +9,7 @@ Usage: lighthouse [OPTIONS] [COMMAND]
|
||||
Commands:
|
||||
account_manager
|
||||
Utilities for generating and managing Ethereum 2.0 accounts. [aliases:
|
||||
a, am, account, account_manager]
|
||||
a, am, account]
|
||||
beacon_node
|
||||
The primary component which connects to the Ethereum 2.0 P2P network
|
||||
and downloads, verifies and stores blocks. Provides a HTTP API for
|
||||
@@ -30,7 +30,7 @@ Commands:
|
||||
validator]
|
||||
validator_manager
|
||||
Utilities for managing a Lighthouse validator client via the HTTP API.
|
||||
[aliases: vm, validator-manager, validator_manager]
|
||||
[aliases: vm, validator-manager]
|
||||
help
|
||||
Print this message or the help of the given subcommand(s)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "boot_node"
|
||||
version = "5.1.3"
|
||||
version = "5.2.0"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = { workspace = true }
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ pub const VERSION: &str = git_version!(
|
||||
// NOTE: using --match instead of --exclude for compatibility with old Git
|
||||
"--match=thiswillnevermatchlol"
|
||||
],
|
||||
prefix = "Lighthouse/v5.1.3-",
|
||||
fallback = "Lighthouse/v5.1.3"
|
||||
prefix = "Lighthouse/v5.2.0-",
|
||||
fallback = "Lighthouse/v5.2.0"
|
||||
);
|
||||
|
||||
/// Returns the first eight characters of the latest commit hash for this build.
|
||||
|
||||
@@ -123,12 +123,10 @@ impl Serializer for ToSendSerializer {
|
||||
take(&mut self.kv, |kv| Box::new((kv, SingleKV(key, val))));
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(integer128)]
|
||||
fn emit_u128(&mut self, key: Key, val: u128) -> slog::Result {
|
||||
take(&mut self.kv, |kv| Box::new((kv, SingleKV(key, val))));
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(integer128)]
|
||||
fn emit_i128(&mut self, key: Key, val: i128) -> slog::Result {
|
||||
take(&mut self.kv, |kv| Box::new((kv, SingleKV(key, val))));
|
||||
Ok(())
|
||||
|
||||
@@ -39,8 +39,6 @@ pub enum Error {
|
||||
/// generally caused by supplying an `amount` at deposit-time that is different to the one used
|
||||
/// at generation-time.
|
||||
Eth1DepositRootMismatch,
|
||||
#[cfg(feature = "unencrypted_keys")]
|
||||
SszKeypairError(String),
|
||||
}
|
||||
|
||||
/// Information required to submit a deposit to the Eth1 deposit contract.
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark};
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use swap_or_not_shuffle::{compute_shuffled_index, shuffle_list as fast_shuffle};
|
||||
|
||||
const SHUFFLE_ROUND_COUNT: u8 = 90;
|
||||
@@ -25,70 +22,32 @@ fn shuffles(c: &mut Criterion) {
|
||||
b.iter(|| black_box(shuffle_list(&seed, 8)))
|
||||
});
|
||||
|
||||
c.bench(
|
||||
"whole list shuffle",
|
||||
Benchmark::new("8 elements", move |b| {
|
||||
for size in [8, 16, 512, 16_384] {
|
||||
c.bench_with_input(
|
||||
BenchmarkId::new("whole list shuffle", format!("{size} elements")),
|
||||
&size,
|
||||
move |b, &n| {
|
||||
let seed = vec![42; 32];
|
||||
b.iter(|| black_box(shuffle_list(&seed, 8)))
|
||||
}),
|
||||
b.iter(|| black_box(shuffle_list(&seed, n)))
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
c.bench(
|
||||
"whole list shuffle",
|
||||
Benchmark::new("16 elements", move |b| {
|
||||
let mut group = c.benchmark_group("fast");
|
||||
group.sample_size(10);
|
||||
for size in [512, 16_384, 4_000_000] {
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("whole list shuffle", format!("{size} elements")),
|
||||
&size,
|
||||
move |b, &n| {
|
||||
let seed = vec![42; 32];
|
||||
b.iter(|| black_box(shuffle_list(&seed, 16)))
|
||||
}),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"whole list shuffle",
|
||||
Benchmark::new("512 elements", move |b| {
|
||||
let seed = vec![42; 32];
|
||||
b.iter(|| black_box(shuffle_list(&seed, 512)))
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"_fast_ whole list shuffle",
|
||||
Benchmark::new("512 elements", move |b| {
|
||||
let seed = vec![42; 32];
|
||||
let list: Vec<usize> = (0..512).collect();
|
||||
let list: Vec<usize> = (0..n).collect();
|
||||
b.iter(|| black_box(fast_shuffle(list.clone(), SHUFFLE_ROUND_COUNT, &seed, true)))
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"whole list shuffle",
|
||||
Benchmark::new("16384 elements", move |b| {
|
||||
let seed = vec![42; 32];
|
||||
b.iter(|| black_box(shuffle_list(&seed, 16_384)))
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"_fast_ whole list shuffle",
|
||||
Benchmark::new("16384 elements", move |b| {
|
||||
let seed = vec![42; 32];
|
||||
let list: Vec<usize> = (0..16384).collect();
|
||||
b.iter(|| black_box(fast_shuffle(list.clone(), SHUFFLE_ROUND_COUNT, &seed, true)))
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
c.bench(
|
||||
"_fast_ whole list shuffle",
|
||||
Benchmark::new("4m elements", move |b| {
|
||||
let seed = vec![42; 32];
|
||||
let list: Vec<usize> = (0..4_000_000).collect();
|
||||
b.iter(|| black_box(fast_shuffle(list.clone(), SHUFFLE_ROUND_COUNT, &seed, true)))
|
||||
})
|
||||
.sample_size(10),
|
||||
},
|
||||
);
|
||||
}
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, shuffles,);
|
||||
criterion_group!(benches, shuffles);
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark};
|
||||
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
|
||||
use milhouse::List;
|
||||
use rayon::prelude::*;
|
||||
use ssz::Encode;
|
||||
@@ -53,75 +50,82 @@ fn all_benches(c: &mut Criterion) {
|
||||
let validator_count = 16_384;
|
||||
let spec = Arc::new(MainnetEthSpec::default_spec());
|
||||
|
||||
let mut g = c.benchmark_group("types");
|
||||
g.sample_size(10);
|
||||
|
||||
let mut state = get_state::<MainnetEthSpec>(validator_count);
|
||||
state.build_caches(&spec).expect("should build caches");
|
||||
let state_bytes = state.as_ssz_bytes();
|
||||
|
||||
let inner_state = state.clone();
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new("encode/beacon_state", move |b| {
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new("encode/beacon_state", validator_count),
|
||||
&inner_state,
|
||||
|b, state| {
|
||||
b.iter_batched_ref(
|
||||
|| inner_state.clone(),
|
||||
|| state.clone(),
|
||||
|state| black_box(state.as_ssz_bytes()),
|
||||
criterion::BatchSize::SmallInput,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
},
|
||||
);
|
||||
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new("decode/beacon_state", move |b| {
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new("decode/beacon_state", validator_count),
|
||||
&(state_bytes.clone(), spec.clone()),
|
||||
|b, (bytes, spec)| {
|
||||
b.iter_batched_ref(
|
||||
|| (state_bytes.clone(), spec.clone()),
|
||||
|| (bytes.clone(), spec.clone()),
|
||||
|(bytes, spec)| {
|
||||
let state: BeaconState<MainnetEthSpec> =
|
||||
BeaconState::from_ssz_bytes(&bytes, &spec).expect("should decode");
|
||||
black_box(state)
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
let inner_state = state.clone();
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new("clone/beacon_state", move |b| {
|
||||
b.iter_batched_ref(
|
||||
|| inner_state.clone(),
|
||||
|state| black_box(state.clone()),
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
);
|
||||
|
||||
let inner_state = state.clone();
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new(
|
||||
"initialized_cached_tree_hash_without_changes/beacon_state",
|
||||
move |b| {
|
||||
b.iter_batched_ref(
|
||||
|| inner_state.clone(),
|
||||
|state| black_box(state.update_tree_hash_cache()),
|
||||
criterion::BatchSize::SmallInput,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let inner_state = state.clone();
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new("clone/beacon_state", validator_count),
|
||||
&inner_state,
|
||||
|b, state| {
|
||||
b.iter_batched_ref(
|
||||
|| state.clone(),
|
||||
|state| black_box(state.clone()),
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
.sample_size(10),
|
||||
},
|
||||
);
|
||||
|
||||
let inner_state = state.clone();
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new(
|
||||
"initialized_cached_tree_hash_without_changes/beacon_state",
|
||||
validator_count,
|
||||
),
|
||||
&inner_state,
|
||||
|b, state| {
|
||||
b.iter_batched_ref(
|
||||
|| state.clone(),
|
||||
|state| black_box(state.update_tree_hash_cache()),
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let mut inner_state = state.clone();
|
||||
inner_state.drop_all_caches().unwrap();
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new("non_initialized_cached_tree_hash/beacon_state", move |b| {
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new(
|
||||
"non_initialized_cached_tree_hash/beacon_state",
|
||||
validator_count,
|
||||
),
|
||||
&inner_state,
|
||||
|b, state| {
|
||||
b.iter_batched_ref(
|
||||
|| inner_state.clone(),
|
||||
|| state.clone(),
|
||||
|state| {
|
||||
black_box(
|
||||
state
|
||||
@@ -129,26 +133,27 @@ fn all_benches(c: &mut Criterion) {
|
||||
.expect("should update tree hash"),
|
||||
)
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
})
|
||||
.sample_size(10),
|
||||
},
|
||||
);
|
||||
|
||||
let inner_state = state.clone();
|
||||
c.bench(
|
||||
&format!("{}_validators", validator_count),
|
||||
Benchmark::new(
|
||||
g.bench_with_input(
|
||||
BenchmarkId::new(
|
||||
"initialized_cached_tree_hash_with_new_validators/beacon_state",
|
||||
move |b| {
|
||||
validator_count,
|
||||
),
|
||||
&inner_state,
|
||||
|b, state| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let mut state = inner_state.clone();
|
||||
let mut state = state.clone();
|
||||
for _ in 0..16 {
|
||||
state
|
||||
.validators_mut()
|
||||
.push(Validator::default())
|
||||
.expect("should push validatorj");
|
||||
.expect("should push validator");
|
||||
state
|
||||
.balances_mut()
|
||||
.push(32_000_000_000)
|
||||
@@ -157,13 +162,11 @@ fn all_benches(c: &mut Criterion) {
|
||||
state
|
||||
},
|
||||
|state| black_box(state.update_tree_hash_cache()),
|
||||
criterion::BatchSize::SmallInput,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
)
|
||||
.sample_size(10),
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, all_benches,);
|
||||
criterion_group!(benches, all_benches);
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
use derivative::Derivative;
|
||||
use merkle_proof::{MerkleTree, MerkleTreeError};
|
||||
use metastruct::metastruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::marker::PhantomData;
|
||||
@@ -50,6 +51,14 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11;
|
||||
),
|
||||
arbitrary(bound = "E: EthSpec, Payload: AbstractExecPayload<E>"),
|
||||
),
|
||||
specific_variant_attributes(
|
||||
Base(metastruct(mappings(beacon_block_body_base_fields(groups(fields))))),
|
||||
Altair(metastruct(mappings(beacon_block_body_altair_fields(groups(fields))))),
|
||||
Bellatrix(metastruct(mappings(beacon_block_body_bellatrix_fields(groups(fields))))),
|
||||
Capella(metastruct(mappings(beacon_block_body_capella_fields(groups(fields))))),
|
||||
Deneb(metastruct(mappings(beacon_block_body_deneb_fields(groups(fields))))),
|
||||
Electra(metastruct(mappings(beacon_block_body_electra_fields(groups(fields))))),
|
||||
),
|
||||
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||
)]
|
||||
@@ -108,6 +117,7 @@ pub struct BeaconBlockBody<E: EthSpec, Payload: AbstractExecPayload<E> = FullPay
|
||||
#[superstruct(only(Electra))]
|
||||
pub consolidations: VariableList<SignedConsolidation, E::MaxConsolidations>,
|
||||
#[superstruct(only(Base, Altair))]
|
||||
#[metastruct(exclude_from(fields))]
|
||||
#[ssz(skip_serializing, skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[serde(skip)]
|
||||
@@ -132,17 +142,43 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
|
||||
}
|
||||
}
|
||||
|
||||
fn body_merkle_leaves(&self) -> Vec<Hash256> {
|
||||
let mut leaves = vec![];
|
||||
match self {
|
||||
Self::Base(body) => {
|
||||
beacon_block_body_base_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
Self::Altair(body) => {
|
||||
beacon_block_body_altair_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
Self::Bellatrix(body) => {
|
||||
beacon_block_body_bellatrix_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
Self::Capella(body) => {
|
||||
beacon_block_body_capella_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
Self::Deneb(body) => {
|
||||
beacon_block_body_deneb_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
Self::Electra(body) => {
|
||||
beacon_block_body_electra_fields!(body, |_, field| leaves
|
||||
.push(field.tree_hash_root()));
|
||||
}
|
||||
}
|
||||
leaves
|
||||
}
|
||||
|
||||
/// Produces the proof of inclusion for a `KzgCommitment` in `self.blob_kzg_commitments`
|
||||
/// at `index`.
|
||||
pub fn kzg_commitment_merkle_proof(
|
||||
&self,
|
||||
index: usize,
|
||||
) -> Result<FixedVector<Hash256, E::KzgCommitmentInclusionProofDepth>, Error> {
|
||||
match self {
|
||||
Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => {
|
||||
Err(Error::IncorrectStateVariant)
|
||||
}
|
||||
Self::Deneb(body) => {
|
||||
// We compute the branches by generating 2 merkle trees:
|
||||
// 1. Merkle tree for the `blob_kzg_commitments` List object
|
||||
// 2. Merkle tree for the `BeaconBlockBody` container
|
||||
@@ -151,21 +187,21 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
|
||||
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
|
||||
//
|
||||
// Branches for `blob_kzg_commitments` without length mix-in
|
||||
let blob_leaves = self
|
||||
.blob_kzg_commitments()?
|
||||
.iter()
|
||||
.map(|commitment| commitment.tree_hash_root())
|
||||
.collect::<Vec<_>>();
|
||||
let depth = E::max_blob_commitments_per_block()
|
||||
.next_power_of_two()
|
||||
.ilog2();
|
||||
let leaves: Vec<_> = body
|
||||
.blob_kzg_commitments
|
||||
.iter()
|
||||
.map(|commitment| commitment.tree_hash_root())
|
||||
.collect();
|
||||
let tree = MerkleTree::create(&leaves, depth as usize);
|
||||
let tree = MerkleTree::create(&blob_leaves, depth as usize);
|
||||
let (_, mut proof) = tree
|
||||
.generate_proof(index, depth as usize)
|
||||
.map_err(Error::MerkleTreeError)?;
|
||||
|
||||
// Add the branch corresponding to the length mix-in.
|
||||
let length = body.blob_kzg_commitments.len();
|
||||
let length = blob_leaves.len();
|
||||
let usize_len = std::mem::size_of::<usize>();
|
||||
let mut length_bytes = [0; BYTES_PER_CHUNK];
|
||||
length_bytes
|
||||
@@ -177,95 +213,18 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
|
||||
|
||||
// Part 2
|
||||
// Branches for `BeaconBlockBody` container
|
||||
let leaves = [
|
||||
body.randao_reveal.tree_hash_root(),
|
||||
body.eth1_data.tree_hash_root(),
|
||||
body.graffiti.tree_hash_root(),
|
||||
body.proposer_slashings.tree_hash_root(),
|
||||
body.attester_slashings.tree_hash_root(),
|
||||
body.attestations.tree_hash_root(),
|
||||
body.deposits.tree_hash_root(),
|
||||
body.voluntary_exits.tree_hash_root(),
|
||||
body.sync_aggregate.tree_hash_root(),
|
||||
body.execution_payload.tree_hash_root(),
|
||||
body.bls_to_execution_changes.tree_hash_root(),
|
||||
body.blob_kzg_commitments.tree_hash_root(),
|
||||
];
|
||||
let beacon_block_body_depth = leaves.len().next_power_of_two().ilog2() as usize;
|
||||
let tree = MerkleTree::create(&leaves, beacon_block_body_depth);
|
||||
let body_leaves = self.body_merkle_leaves();
|
||||
let beacon_block_body_depth = body_leaves.len().next_power_of_two().ilog2() as usize;
|
||||
let tree = MerkleTree::create(&body_leaves, beacon_block_body_depth);
|
||||
let (_, mut proof_body) = tree
|
||||
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
|
||||
.map_err(Error::MerkleTreeError)?;
|
||||
// Join the proofs for the subtree and the main tree
|
||||
proof.append(&mut proof_body);
|
||||
|
||||
debug_assert_eq!(proof.len(), E::kzg_proof_inclusion_proof_depth());
|
||||
|
||||
Ok(proof.into())
|
||||
}
|
||||
// TODO(electra): De-duplicate proof computation.
|
||||
Self::Electra(body) => {
|
||||
// We compute the branches by generating 2 merkle trees:
|
||||
// 1. Merkle tree for the `blob_kzg_commitments` List object
|
||||
// 2. Merkle tree for the `BeaconBlockBody` container
|
||||
// We then merge the branches for both the trees all the way up to the root.
|
||||
|
||||
// Part1 (Branches for the subtree rooted at `blob_kzg_commitments`)
|
||||
//
|
||||
// Branches for `blob_kzg_commitments` without length mix-in
|
||||
let depth = E::max_blob_commitments_per_block()
|
||||
.next_power_of_two()
|
||||
.ilog2();
|
||||
let leaves: Vec<_> = body
|
||||
.blob_kzg_commitments
|
||||
.iter()
|
||||
.map(|commitment| commitment.tree_hash_root())
|
||||
.collect();
|
||||
let tree = MerkleTree::create(&leaves, depth as usize);
|
||||
let (_, mut proof) = tree
|
||||
.generate_proof(index, depth as usize)
|
||||
.map_err(Error::MerkleTreeError)?;
|
||||
|
||||
// Add the branch corresponding to the length mix-in.
|
||||
let length = body.blob_kzg_commitments.len();
|
||||
let usize_len = std::mem::size_of::<usize>();
|
||||
let mut length_bytes = [0; BYTES_PER_CHUNK];
|
||||
length_bytes
|
||||
.get_mut(0..usize_len)
|
||||
.ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))?
|
||||
.copy_from_slice(&length.to_le_bytes());
|
||||
let length_root = Hash256::from_slice(length_bytes.as_slice());
|
||||
proof.push(length_root);
|
||||
|
||||
// Part 2
|
||||
// Branches for `BeaconBlockBody` container
|
||||
let leaves = [
|
||||
body.randao_reveal.tree_hash_root(),
|
||||
body.eth1_data.tree_hash_root(),
|
||||
body.graffiti.tree_hash_root(),
|
||||
body.proposer_slashings.tree_hash_root(),
|
||||
body.attester_slashings.tree_hash_root(),
|
||||
body.attestations.tree_hash_root(),
|
||||
body.deposits.tree_hash_root(),
|
||||
body.voluntary_exits.tree_hash_root(),
|
||||
body.sync_aggregate.tree_hash_root(),
|
||||
body.execution_payload.tree_hash_root(),
|
||||
body.bls_to_execution_changes.tree_hash_root(),
|
||||
body.blob_kzg_commitments.tree_hash_root(),
|
||||
body.consolidations.tree_hash_root(),
|
||||
];
|
||||
let beacon_block_body_depth = leaves.len().next_power_of_two().ilog2() as usize;
|
||||
let tree = MerkleTree::create(&leaves, beacon_block_body_depth);
|
||||
let (_, mut proof_body) = tree
|
||||
.generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth)
|
||||
.map_err(Error::MerkleTreeError)?;
|
||||
// Join the proofs for the subtree and the main tree
|
||||
proof.append(&mut proof_body);
|
||||
|
||||
debug_assert_eq!(proof.len(), E::kzg_proof_inclusion_proof_depth());
|
||||
Ok(proof.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if this block body has a non-zero number of blobs.
|
||||
pub fn has_blobs(self) -> bool {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lcli"
|
||||
description = "Lighthouse CLI (modeled after zcli)"
|
||||
version = "5.1.3"
|
||||
version = "5.2.0"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -24,17 +24,14 @@ ethereum_hashing = { workspace = true }
|
||||
ethereum_ssz = { workspace = true }
|
||||
environment = { workspace = true }
|
||||
eth2_network_config = { workspace = true }
|
||||
genesis = { workspace = true }
|
||||
deposit_contract = { workspace = true }
|
||||
tree_hash = { workspace = true }
|
||||
clap_utils = { workspace = true }
|
||||
lighthouse_network = { workspace = true }
|
||||
validator_dir = { workspace = true, features = ["insecure_keys"] }
|
||||
validator_dir = { workspace = true }
|
||||
lighthouse_version = { workspace = true }
|
||||
account_utils = { workspace = true }
|
||||
eth2_wallet = { workspace = true }
|
||||
eth1_test_rig = { workspace = true }
|
||||
sensitive_url = { workspace = true }
|
||||
eth2 = { workspace = true }
|
||||
snap = { workspace = true }
|
||||
beacon_chain = { workspace = true }
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use eth2_network_config::Eth2NetworkConfig;
|
||||
use ssz::Encode;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use types::{BeaconState, EthSpec};
|
||||
|
||||
pub fn run<E: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), String> {
|
||||
let path = matches
|
||||
.get_one::<String>("ssz-state")
|
||||
.ok_or("ssz-state not specified")?
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| format!("Unable to parse ssz-state: {}", e))?;
|
||||
|
||||
let genesis_time = matches
|
||||
.get_one::<String>("genesis-time")
|
||||
.ok_or("genesis-time not specified")?
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Unable to parse genesis-time: {}", e))?;
|
||||
|
||||
let eth2_network_config = Eth2NetworkConfig::load(testnet_dir)?;
|
||||
let spec = ð2_network_config.chain_spec::<E>()?;
|
||||
|
||||
let mut state: BeaconState<E> = {
|
||||
let mut file = File::open(&path).map_err(|e| format!("Unable to open file: {}", e))?;
|
||||
|
||||
let mut ssz = vec![];
|
||||
|
||||
file.read_to_end(&mut ssz)
|
||||
.map_err(|e| format!("Unable to read file: {}", e))?;
|
||||
|
||||
BeaconState::from_ssz_bytes(&ssz, spec)
|
||||
.map_err(|e| format!("Unable to decode SSZ: {:?}", e))?
|
||||
};
|
||||
|
||||
*state.genesis_time_mut() = genesis_time;
|
||||
|
||||
let mut file = File::create(path).map_err(|e| format!("Unable to create file: {}", e))?;
|
||||
|
||||
file.write_all(&state.as_ssz_bytes())
|
||||
.map_err(|e| format!("Unable to write to file: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use clap_utils::{parse_optional, parse_required};
|
||||
use ssz::Encode;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use types::{
|
||||
EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix,
|
||||
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra,
|
||||
ForkName,
|
||||
};
|
||||
|
||||
pub fn run<E: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
|
||||
let eth1_block_hash = parse_required(matches, "execution-block-hash")?;
|
||||
let genesis_time = parse_optional(matches, "genesis-time")?.unwrap_or(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| format!("Unable to get time: {:?}", e))?
|
||||
.as_secs(),
|
||||
);
|
||||
let base_fee_per_gas = parse_required(matches, "base-fee-per-gas")?;
|
||||
let gas_limit = parse_required(matches, "gas-limit")?;
|
||||
let file_name = matches
|
||||
.get_one::<String>("file")
|
||||
.ok_or("No file supplied")?;
|
||||
let fork_name: ForkName = parse_optional(matches, "fork")?.unwrap_or(ForkName::Bellatrix);
|
||||
|
||||
let execution_payload_header: ExecutionPayloadHeader<E> = match fork_name {
|
||||
ForkName::Base | ForkName::Altair => return Err("invalid fork name".to_string()),
|
||||
ForkName::Bellatrix => ExecutionPayloadHeader::Bellatrix(ExecutionPayloadHeaderBellatrix {
|
||||
gas_limit,
|
||||
base_fee_per_gas,
|
||||
timestamp: genesis_time,
|
||||
block_hash: eth1_block_hash,
|
||||
prev_randao: eth1_block_hash.into_root(),
|
||||
..ExecutionPayloadHeaderBellatrix::default()
|
||||
}),
|
||||
ForkName::Capella => ExecutionPayloadHeader::Capella(ExecutionPayloadHeaderCapella {
|
||||
gas_limit,
|
||||
base_fee_per_gas,
|
||||
timestamp: genesis_time,
|
||||
block_hash: eth1_block_hash,
|
||||
prev_randao: eth1_block_hash.into_root(),
|
||||
..ExecutionPayloadHeaderCapella::default()
|
||||
}),
|
||||
ForkName::Deneb => ExecutionPayloadHeader::Deneb(ExecutionPayloadHeaderDeneb {
|
||||
gas_limit,
|
||||
base_fee_per_gas,
|
||||
timestamp: genesis_time,
|
||||
block_hash: eth1_block_hash,
|
||||
prev_randao: eth1_block_hash.into_root(),
|
||||
..ExecutionPayloadHeaderDeneb::default()
|
||||
}),
|
||||
ForkName::Electra => ExecutionPayloadHeader::Electra(ExecutionPayloadHeaderElectra {
|
||||
gas_limit,
|
||||
base_fee_per_gas,
|
||||
timestamp: genesis_time,
|
||||
block_hash: eth1_block_hash,
|
||||
prev_randao: eth1_block_hash.into_root(),
|
||||
..ExecutionPayloadHeaderElectra::default()
|
||||
}),
|
||||
};
|
||||
|
||||
let mut file = File::create(file_name).map_err(|_| "Unable to create file".to_string())?;
|
||||
let bytes = execution_payload_header.as_ssz_bytes();
|
||||
file.write_all(bytes.as_slice())
|
||||
.map_err(|_| "Unable to write to file".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use environment::Environment;
|
||||
use types::EthSpec;
|
||||
|
||||
use eth1_test_rig::{Http, Provider};
|
||||
|
||||
pub fn run<E: EthSpec>(env: Environment<E>, matches: &ArgMatches) -> Result<(), String> {
|
||||
let eth1_http: String = clap_utils::parse_required(matches, "eth1-http")?;
|
||||
let confirmations: usize = clap_utils::parse_required(matches, "confirmations")?;
|
||||
let validator_count: Option<usize> = clap_utils::parse_optional(matches, "validator-count")?;
|
||||
|
||||
let client = Provider::<Http>::try_from(ð1_http)
|
||||
.map_err(|e| format!("Unable to connect to eth1 HTTP: {:?}", e))?;
|
||||
|
||||
env.runtime().block_on(async {
|
||||
let contract = eth1_test_rig::DepositContract::deploy(client, confirmations, None)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to deploy deposit contract: {:?}", e))?;
|
||||
|
||||
println!("Deposit contract address: {:?}", contract.address());
|
||||
|
||||
// Deposit insecure validators to the deposit contract created
|
||||
if let Some(validator_count) = validator_count {
|
||||
let amount = env.eth2_config.spec.max_effective_balance;
|
||||
for i in 0..validator_count {
|
||||
println!("Submitting deposit for validator {}...", i);
|
||||
contract.deposit_deterministic_async::<E>(i, amount).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use environment::Environment;
|
||||
use eth2_network_config::Eth2NetworkConfig;
|
||||
use genesis::{Eth1Config, Eth1Endpoint, Eth1GenesisService};
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use ssz::Encode;
|
||||
use std::cmp::max;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use types::EthSpec;
|
||||
|
||||
/// Interval between polling the eth1 node for genesis information.
|
||||
pub const ETH1_GENESIS_UPDATE_INTERVAL: Duration = Duration::from_millis(7_000);
|
||||
|
||||
pub fn run<E: EthSpec>(
|
||||
env: Environment<E>,
|
||||
testnet_dir: PathBuf,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(), String> {
|
||||
let endpoints = matches
|
||||
.get_one::<String>("eth1-endpoint")
|
||||
.map(|e| {
|
||||
warn!("The --eth1-endpoint flag is deprecated. Please use --eth1-endpoints instead");
|
||||
String::from(e)
|
||||
})
|
||||
.or_else(|| {
|
||||
matches
|
||||
.get_one::<String>("eth1-endpoints")
|
||||
.map(String::from)
|
||||
});
|
||||
|
||||
let mut eth2_network_config = Eth2NetworkConfig::load(testnet_dir.clone())?;
|
||||
|
||||
let spec = eth2_network_config.chain_spec::<E>()?;
|
||||
|
||||
let mut config = Eth1Config::default();
|
||||
if let Some(v) = endpoints.clone() {
|
||||
let endpoint = SensitiveUrl::parse(&v)
|
||||
.map_err(|e| format!("Unable to parse eth1 endpoint URL: {:?}", e))?;
|
||||
config.endpoint = Eth1Endpoint::NoAuth(endpoint);
|
||||
}
|
||||
config.deposit_contract_address = format!("{:?}", spec.deposit_contract_address);
|
||||
config.deposit_contract_deploy_block = eth2_network_config.deposit_contract_deploy_block;
|
||||
config.lowest_cached_block_number = eth2_network_config.deposit_contract_deploy_block;
|
||||
config.follow_distance = spec.eth1_follow_distance / 2;
|
||||
config.node_far_behind_seconds = max(5, config.follow_distance) * spec.seconds_per_eth1_block;
|
||||
|
||||
let genesis_service =
|
||||
Eth1GenesisService::new(config, env.core_context().log().clone(), spec.clone())?;
|
||||
|
||||
env.runtime().block_on(async {
|
||||
let _ = genesis_service
|
||||
.wait_for_genesis_state::<E>(ETH1_GENESIS_UPDATE_INTERVAL, spec)
|
||||
.await
|
||||
.map(move |genesis_state| {
|
||||
eth2_network_config.genesis_state_bytes = Some(genesis_state.as_ssz_bytes().into());
|
||||
eth2_network_config.force_write_to_file(testnet_dir)
|
||||
})
|
||||
.map_err(|e| format!("Failed to find genesis: {}", e))?;
|
||||
|
||||
info!("Starting service to produce genesis BeaconState from eth1");
|
||||
info!("Connecting to eth1 http endpoints: {:?}", endpoints);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use validator_dir::Builder as ValidatorBuilder;
|
||||
|
||||
/// Generates validator directories with INSECURE, deterministic keypairs given the range
|
||||
/// of indices, validator and secret directories.
|
||||
pub fn generate_validator_dirs(
|
||||
indices: &[usize],
|
||||
validators_dir: PathBuf,
|
||||
secrets_dir: PathBuf,
|
||||
) -> Result<(), String> {
|
||||
if !validators_dir.exists() {
|
||||
fs::create_dir_all(&validators_dir)
|
||||
.map_err(|e| format!("Unable to create validators dir: {:?}", e))?;
|
||||
}
|
||||
|
||||
if !secrets_dir.exists() {
|
||||
fs::create_dir_all(&secrets_dir)
|
||||
.map_err(|e| format!("Unable to create secrets dir: {:?}", e))?;
|
||||
}
|
||||
|
||||
for i in indices {
|
||||
println!("Validator {}", i + 1);
|
||||
|
||||
ValidatorBuilder::new(validators_dir.clone())
|
||||
.password_dir(secrets_dir.clone())
|
||||
.store_withdrawal_keystore(false)
|
||||
.insecure_voting_keypair(*i)
|
||||
.map_err(|e| format!("Unable to generate keys: {:?}", e))?
|
||||
.build()
|
||||
.map_err(|e| format!("Unable to build validator: {:?}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(matches: &ArgMatches) -> Result<(), String> {
|
||||
let validator_count: usize = clap_utils::parse_required(matches, "count")?;
|
||||
let base_dir: PathBuf = clap_utils::parse_required(matches, "base-dir")?;
|
||||
let node_count: Option<usize> = clap_utils::parse_optional(matches, "node-count")?;
|
||||
if let Some(node_count) = node_count {
|
||||
let validators_per_node = validator_count / node_count;
|
||||
let validator_range = (0..validator_count).collect::<Vec<_>>();
|
||||
let indices_range = validator_range
|
||||
.chunks(validators_per_node)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, indices) in indices_range.iter().enumerate() {
|
||||
let validators_dir = base_dir.join(format!("node_{}", i + 1)).join("validators");
|
||||
let secrets_dir = base_dir.join(format!("node_{}", i + 1)).join("secrets");
|
||||
generate_validator_dirs(indices, validators_dir, secrets_dir)?;
|
||||
}
|
||||
} else {
|
||||
let validators_dir = base_dir.join("validators");
|
||||
let secrets_dir = base_dir.join("secrets");
|
||||
generate_validator_dirs(
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
validators_dir,
|
||||
secrets_dir,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
use clap::ArgMatches;
|
||||
use clap_utils::parse_ssz_optional;
|
||||
use eth2_network_config::Eth2NetworkConfig;
|
||||
use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
|
||||
use ssz::Encode;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use types::{test_utils::generate_deterministic_keypairs, EthSpec, Hash256};
|
||||
|
||||
pub fn run<E: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), String> {
|
||||
let validator_count = matches
|
||||
.get_one::<String>("validator-count")
|
||||
.ok_or("validator-count not specified")?
|
||||
.parse::<usize>()
|
||||
.map_err(|e| format!("Unable to parse validator-count: {}", e))?;
|
||||
|
||||
let genesis_time = if let Some(genesis_time) = matches.get_one::<String>("genesis-time") {
|
||||
genesis_time
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Unable to parse genesis-time: {}", e))?
|
||||
} else {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| format!("Unable to get time: {:?}", e))?
|
||||
.as_secs()
|
||||
};
|
||||
|
||||
let mut eth2_network_config = Eth2NetworkConfig::load(testnet_dir.clone())?;
|
||||
|
||||
let mut spec = eth2_network_config.chain_spec::<E>()?;
|
||||
|
||||
if let Some(v) = parse_ssz_optional(matches, "genesis-fork-version")? {
|
||||
spec.genesis_fork_version = v;
|
||||
}
|
||||
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
let genesis_state = interop_genesis_state::<E>(
|
||||
&keypairs,
|
||||
genesis_time,
|
||||
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
|
||||
None,
|
||||
&spec,
|
||||
)?;
|
||||
|
||||
eth2_network_config.genesis_state_bytes = Some(genesis_state.as_ssz_bytes().into());
|
||||
eth2_network_config.force_write_to_file(testnet_dir)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
557
lcli/src/main.rs
557
lcli/src/main.rs
@@ -1,20 +1,12 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
mod block_root;
|
||||
mod change_genesis_time;
|
||||
mod check_deposit_data;
|
||||
mod create_payload_header;
|
||||
mod deploy_deposit_contract;
|
||||
mod eth1_genesis;
|
||||
mod generate_bootnode_enr;
|
||||
mod indexed_attestations;
|
||||
mod insecure_validators;
|
||||
mod interop_genesis;
|
||||
mod mnemonic_validators;
|
||||
mod mock_el;
|
||||
mod new_testnet;
|
||||
mod parse_ssz;
|
||||
mod replace_state_pubkeys;
|
||||
mod skip_slots;
|
||||
mod state_root;
|
||||
mod transition_blocks;
|
||||
@@ -272,489 +264,6 @@ fn main() {
|
||||
.display_order(0)
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("deploy-deposit-contract")
|
||||
.about(
|
||||
"Deploy a testing eth1 deposit contract.",
|
||||
)
|
||||
.arg(
|
||||
Arg::new("eth1-http")
|
||||
.long("eth1-http")
|
||||
.short('e')
|
||||
.value_name("ETH1_HTTP_PATH")
|
||||
.help("Path to an Eth1 JSON-RPC IPC endpoint")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("confirmations")
|
||||
.value_name("INTEGER")
|
||||
.long("confirmations")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("3")
|
||||
.help("The number of block confirmations before declaring the contract deployed.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("validator-count")
|
||||
.value_name("VALIDATOR_COUNT")
|
||||
.long("validator-count")
|
||||
.action(ArgAction::Set)
|
||||
.help("If present, makes `validator_count` number of INSECURE deterministic deposits after \
|
||||
deploying the deposit contract."
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("eth1-genesis")
|
||||
.about("Listens to the eth1 chain and finds the genesis beacon state")
|
||||
.arg(
|
||||
Arg::new("eth1-endpoint")
|
||||
.short('e')
|
||||
.long("eth1-endpoint")
|
||||
.value_name("HTTP_SERVER")
|
||||
.action(ArgAction::Set)
|
||||
.help("Deprecated. Use --eth1-endpoints.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("eth1-endpoints")
|
||||
.long("eth1-endpoints")
|
||||
.value_name("HTTP_SERVER_LIST")
|
||||
.action(ArgAction::Set)
|
||||
.conflicts_with("eth1-endpoint")
|
||||
.help(
|
||||
"One or more comma-delimited URLs to eth1 JSON-RPC http APIs. \
|
||||
If multiple endpoints are given the endpoints are used as \
|
||||
fallback in the given order.",
|
||||
)
|
||||
.display_order(0)
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("interop-genesis")
|
||||
.about("Produces an interop-compatible genesis state using deterministic keypairs")
|
||||
.arg(
|
||||
Arg::new("validator-count")
|
||||
.long("validator-count")
|
||||
.index(1)
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("1024")
|
||||
.help("The number of validators in the genesis state.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-time")
|
||||
.long("genesis-time")
|
||||
.short('t')
|
||||
.value_name("UNIX_EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help("The value for state.genesis_time. Defaults to now.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-fork-version")
|
||||
.long("genesis-fork-version")
|
||||
.value_name("HEX")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"Used to avoid reply attacks between testnets. Recommended to set to
|
||||
non-default.",
|
||||
)
|
||||
.display_order(0)
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("change-genesis-time")
|
||||
.about(
|
||||
"Loads a file with an SSZ-encoded BeaconState and modifies the genesis time.",
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ssz-state")
|
||||
.index(1)
|
||||
.value_name("PATH")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("The path to the SSZ file")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-time")
|
||||
.index(2)
|
||||
.value_name("UNIX_EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("The value for state.genesis_time.")
|
||||
.display_order(0)
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("replace-state-pubkeys")
|
||||
.about(
|
||||
"Loads a file with an SSZ-encoded BeaconState and replaces \
|
||||
all the validator pubkeys with ones derived from the mnemonic \
|
||||
such that validator indices correspond to EIP-2334 voting keypair \
|
||||
derivation paths.",
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ssz-state")
|
||||
.index(1)
|
||||
.value_name("PATH")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("The path to the SSZ file")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mnemonic")
|
||||
.index(2)
|
||||
.value_name("BIP39_MNENMONIC")
|
||||
.action(ArgAction::Set)
|
||||
.default_value(
|
||||
"replace nephew blur decorate waste convince soup column \
|
||||
orient excite play baby",
|
||||
)
|
||||
.help("The mnemonic for key derivation.")
|
||||
.display_order(0)
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("create-payload-header")
|
||||
.about("Generates an SSZ file containing bytes for an `ExecutionPayloadHeader`. \
|
||||
Useful as input for `lcli new-testnet --execution-payload-header FILE`. If `--fork` \
|
||||
is not provided, a payload header for the `Bellatrix` fork will be created.")
|
||||
.arg(
|
||||
Arg::new("execution-block-hash")
|
||||
.long("execution-block-hash")
|
||||
.value_name("BLOCK_HASH")
|
||||
.action(ArgAction::Set)
|
||||
.help("The block hash used when generating an execution payload. This \
|
||||
value is used for `execution_payload_header.block_hash` as well as \
|
||||
`execution_payload_header.random`")
|
||||
.default_value(
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-time")
|
||||
.long("genesis-time")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The genesis time when generating an execution payload.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("base-fee-per-gas")
|
||||
.long("base-fee-per-gas")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The base fee per gas field in the execution payload generated.")
|
||||
.default_value("1000000000")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("gas-limit")
|
||||
.long("gas-limit")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The gas limit field in the execution payload generated.")
|
||||
.default_value("30000000")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("file")
|
||||
.long("file")
|
||||
.value_name("FILE")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("Output file")
|
||||
.display_order(0)
|
||||
).arg(
|
||||
Arg::new("fork")
|
||||
.long("fork")
|
||||
.value_name("FORK")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("bellatrix")
|
||||
.help("The fork for which the execution payload header should be created.")
|
||||
.value_parser(["bellatrix", "capella", "deneb", "electra"])
|
||||
.display_order(0)
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("new-testnet")
|
||||
.about(
|
||||
"Produce a new testnet directory. If any of the optional flags are not
|
||||
supplied the values will remain the default for the --spec flag",
|
||||
)
|
||||
.arg(
|
||||
Arg::new("force")
|
||||
.long("force")
|
||||
.short('f')
|
||||
.action(ArgAction::SetTrue)
|
||||
.help_heading(FLAG_HEADER)
|
||||
.help("Overwrites any previous testnet configurations")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("interop-genesis-state")
|
||||
.long("interop-genesis-state")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help_heading(FLAG_HEADER)
|
||||
.help(
|
||||
"If present, a interop-style genesis.ssz file will be generated.",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("derived-genesis-state")
|
||||
.long("derived-genesis-state")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help_heading(FLAG_HEADER)
|
||||
.help(
|
||||
"If present, a genesis.ssz file will be generated with keys generated from a given mnemonic.",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mnemonic-phrase")
|
||||
.long("mnemonic-phrase")
|
||||
.value_name("MNEMONIC_PHRASE")
|
||||
.action(ArgAction::Set)
|
||||
.requires("derived-genesis-state")
|
||||
.help("The mnemonic with which we generate the validator keys for a derived genesis state")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("min-genesis-time")
|
||||
.long("min-genesis-time")
|
||||
.value_name("UNIX_SECONDS")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The minimum permitted genesis time. For non-eth1 testnets will be
|
||||
the genesis time. Defaults to now.",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("min-genesis-active-validator-count")
|
||||
.long("min-genesis-active-validator-count")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The number of validators required to trigger eth2 genesis.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-delay")
|
||||
.long("genesis-delay")
|
||||
.value_name("SECONDS")
|
||||
.action(ArgAction::Set)
|
||||
.help("The delay between sufficient eth1 deposits and eth2 genesis.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("min-deposit-amount")
|
||||
.long("min-deposit-amount")
|
||||
.value_name("GWEI")
|
||||
.action(ArgAction::Set)
|
||||
.help("The minimum permitted deposit amount.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("max-effective-balance")
|
||||
.long("max-effective-balance")
|
||||
.value_name("GWEI")
|
||||
.action(ArgAction::Set)
|
||||
.help("The amount required to become a validator.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("effective-balance-increment")
|
||||
.long("effective-balance-increment")
|
||||
.value_name("GWEI")
|
||||
.action(ArgAction::Set)
|
||||
.help("The steps in effective balance calculation.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ejection-balance")
|
||||
.long("ejection-balance")
|
||||
.value_name("GWEI")
|
||||
.action(ArgAction::Set)
|
||||
.help("The balance at which a validator gets ejected.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("eth1-follow-distance")
|
||||
.long("eth1-follow-distance")
|
||||
.value_name("ETH1_BLOCKS")
|
||||
.action(ArgAction::Set)
|
||||
.help("The distance to follow behind the eth1 chain head.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-fork-version")
|
||||
.long("genesis-fork-version")
|
||||
.value_name("HEX")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"Used to avoid reply attacks between testnets. Recommended to set to
|
||||
non-default.",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("seconds-per-slot")
|
||||
.long("seconds-per-slot")
|
||||
.value_name("SECONDS")
|
||||
.action(ArgAction::Set)
|
||||
.help("Eth2 slot time")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("seconds-per-eth1-block")
|
||||
.long("seconds-per-eth1-block")
|
||||
.value_name("SECONDS")
|
||||
.action(ArgAction::Set)
|
||||
.help("Eth1 block time")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("eth1-id")
|
||||
.long("eth1-id")
|
||||
.value_name("ETH1_ID")
|
||||
.action(ArgAction::Set)
|
||||
.help("The chain id and network id for the eth1 testnet.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("deposit-contract-address")
|
||||
.long("deposit-contract-address")
|
||||
.value_name("ETH1_ADDRESS")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("The address of the deposit contract.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("deposit-contract-deploy-block")
|
||||
.long("deposit-contract-deploy-block")
|
||||
.value_name("ETH1_BLOCK_NUMBER")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("0")
|
||||
.help(
|
||||
"The block the deposit contract was deployed. Setting this is a huge
|
||||
optimization for nodes, please do it.",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("altair-fork-epoch")
|
||||
.long("altair-fork-epoch")
|
||||
.value_name("EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The epoch at which to enable the Altair hard fork",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bellatrix-fork-epoch")
|
||||
.long("bellatrix-fork-epoch")
|
||||
.value_name("EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The epoch at which to enable the Bellatrix hard fork",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("capella-fork-epoch")
|
||||
.long("capella-fork-epoch")
|
||||
.value_name("EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The epoch at which to enable the Capella hard fork",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("deneb-fork-epoch")
|
||||
.long("deneb-fork-epoch")
|
||||
.value_name("EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The epoch at which to enable the Deneb hard fork",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("electra-fork-epoch")
|
||||
.long("electra-fork-epoch")
|
||||
.value_name("EPOCH")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The epoch at which to enable the Electra hard fork",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("ttd")
|
||||
.long("ttd")
|
||||
.value_name("TTD")
|
||||
.action(ArgAction::Set)
|
||||
.help(
|
||||
"The terminal total difficulty",
|
||||
)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("eth1-block-hash")
|
||||
.long("eth1-block-hash")
|
||||
.value_name("BLOCK_HASH")
|
||||
.action(ArgAction::Set)
|
||||
.help("The eth1 block hash used when generating a genesis state.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("execution-payload-header")
|
||||
.long("execution-payload-header")
|
||||
.value_name("FILE")
|
||||
.action(ArgAction::Set)
|
||||
.required(false)
|
||||
.help("Path to file containing `ExecutionPayloadHeader` SSZ bytes to be \
|
||||
used in the genesis state.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("validator-count")
|
||||
.long("validator-count")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The number of validators when generating a genesis state.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("genesis-time")
|
||||
.long("genesis-time")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The genesis time when generating a genesis state.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("proposer-score-boost")
|
||||
.long("proposer-score-boost")
|
||||
.value_name("INTEGER")
|
||||
.action(ArgAction::Set)
|
||||
.help("The proposer score boost to apply as a percentage, e.g. 70 = 70%")
|
||||
.display_order(0)
|
||||
)
|
||||
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("check-deposit-data")
|
||||
.about("Checks the integrity of some deposit data.")
|
||||
@@ -834,36 +343,6 @@ fn main() {
|
||||
.display_order(0)
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("insecure-validators")
|
||||
.about("Produces validator directories with INSECURE, deterministic keypairs.")
|
||||
.arg(
|
||||
Arg::new("count")
|
||||
.long("count")
|
||||
.value_name("COUNT")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("Produces validators in the range of 0..count.")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("base-dir")
|
||||
.long("base-dir")
|
||||
.value_name("BASE_DIR")
|
||||
.action(ArgAction::Set)
|
||||
.required(true)
|
||||
.help("The base directory where validator keypairs and secrets are stored")
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("node-count")
|
||||
.long("node-count")
|
||||
.value_name("NODE_COUNT")
|
||||
.action(ArgAction::Set)
|
||||
.help("The number of nodes to divide the validator keys to")
|
||||
.display_order(0)
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("mnemonic-validators")
|
||||
.about("Produces validator directories by deriving the keys from \
|
||||
@@ -1128,9 +607,6 @@ fn run<E: EthSpec>(env_builder: EnvironmentBuilder<E>, matches: &ArgMatches) ->
|
||||
(None, Some(network_name))
|
||||
};
|
||||
|
||||
// Lazily load either the testnet dir or the network config, as required.
|
||||
// Some subcommands like new-testnet need the testnet dir but not the network config.
|
||||
let get_testnet_dir = || testnet_dir.clone().ok_or("testnet-dir is required");
|
||||
let get_network_config = || {
|
||||
if let Some(testnet_dir) = &testnet_dir {
|
||||
Eth2NetworkConfig::load(testnet_dir.clone()).map_err(|e| {
|
||||
@@ -1162,43 +638,10 @@ fn run<E: EthSpec>(env_builder: EnvironmentBuilder<E>, matches: &ArgMatches) ->
|
||||
run_parse_ssz::<E>(network_config, matches)
|
||||
.map_err(|e| format!("Failed to pretty print hex: {}", e))
|
||||
}
|
||||
Some(("deploy-deposit-contract", matches)) => {
|
||||
deploy_deposit_contract::run::<E>(env, matches)
|
||||
.map_err(|e| format!("Failed to run deploy-deposit-contract command: {}", e))
|
||||
}
|
||||
Some(("eth1-genesis", matches)) => {
|
||||
let testnet_dir = get_testnet_dir()?;
|
||||
eth1_genesis::run::<E>(env, testnet_dir, matches)
|
||||
.map_err(|e| format!("Failed to run eth1-genesis command: {}", e))
|
||||
}
|
||||
Some(("interop-genesis", matches)) => {
|
||||
let testnet_dir = get_testnet_dir()?;
|
||||
interop_genesis::run::<E>(testnet_dir, matches)
|
||||
.map_err(|e| format!("Failed to run interop-genesis command: {}", e))
|
||||
}
|
||||
Some(("change-genesis-time", matches)) => {
|
||||
let testnet_dir = get_testnet_dir()?;
|
||||
change_genesis_time::run::<E>(testnet_dir, matches)
|
||||
.map_err(|e| format!("Failed to run change-genesis-time command: {}", e))
|
||||
}
|
||||
Some(("create-payload-header", matches)) => create_payload_header::run::<E>(matches)
|
||||
.map_err(|e| format!("Failed to run create-payload-header command: {}", e)),
|
||||
Some(("replace-state-pubkeys", matches)) => {
|
||||
let testnet_dir = get_testnet_dir()?;
|
||||
replace_state_pubkeys::run::<E>(testnet_dir, matches)
|
||||
.map_err(|e| format!("Failed to run replace-state-pubkeys command: {}", e))
|
||||
}
|
||||
Some(("new-testnet", matches)) => {
|
||||
let testnet_dir = get_testnet_dir()?;
|
||||
new_testnet::run::<E>(testnet_dir, matches)
|
||||
.map_err(|e| format!("Failed to run new_testnet command: {}", e))
|
||||
}
|
||||
Some(("check-deposit-data", matches)) => check_deposit_data::run(matches)
|
||||
.map_err(|e| format!("Failed to run check-deposit-data command: {}", e)),
|
||||
Some(("generate-bootnode-enr", matches)) => generate_bootnode_enr::run::<E>(matches)
|
||||
.map_err(|e| format!("Failed to run generate-bootnode-enr command: {}", e)),
|
||||
Some(("insecure-validators", matches)) => insecure_validators::run(matches)
|
||||
.map_err(|e| format!("Failed to run insecure-validators command: {}", e)),
|
||||
Some(("mnemonic-validators", matches)) => mnemonic_validators::run(matches)
|
||||
.map_err(|e| format!("Failed to run mnemonic-validators command: {}", e)),
|
||||
Some(("indexed-attestations", matches)) => indexed_attestations::run::<E>(matches)
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
use account_utils::eth2_keystore::keypair_from_secret;
|
||||
use clap::ArgMatches;
|
||||
use clap_utils::{parse_optional, parse_required, parse_ssz_optional};
|
||||
use eth2_network_config::{Eth2NetworkConfig, GenesisStateSource, TRUSTED_SETUP_BYTES};
|
||||
use eth2_wallet::bip39::Seed;
|
||||
use eth2_wallet::bip39::{Language, Mnemonic};
|
||||
use eth2_wallet::{recover_validator_secret_from_mnemonic, KeyType};
|
||||
use ethereum_hashing::hash;
|
||||
use ssz::Decode;
|
||||
use ssz::Encode;
|
||||
use state_processing::process_activations;
|
||||
use state_processing::upgrade::{
|
||||
upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb,
|
||||
upgrade_to_electra,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use types::ExecutionBlockHash;
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, Address, BeaconState, ChainSpec, Config, Epoch,
|
||||
Eth1Data, EthSpec, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix,
|
||||
ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra,
|
||||
ForkName, Hash256, Keypair, PublicKey, Validator,
|
||||
};
|
||||
|
||||
pub fn run<E: EthSpec>(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Result<(), String> {
|
||||
let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
|
||||
let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?;
|
||||
|
||||
let overwrite_files = matches.get_flag("force");
|
||||
|
||||
if testnet_dir_path.exists() && !overwrite_files {
|
||||
return Err(format!(
|
||||
"{:?} already exists, will not overwrite. Use --force to overwrite",
|
||||
testnet_dir_path
|
||||
));
|
||||
}
|
||||
|
||||
let mut spec = E::default_spec();
|
||||
|
||||
// Update the spec value if the flag was defined. Otherwise, leave it as the default.
|
||||
macro_rules! maybe_update {
|
||||
($flag: tt, $var: ident) => {
|
||||
if let Some(val) = parse_optional(matches, $flag)? {
|
||||
spec.$var = val
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
spec.deposit_contract_address = deposit_contract_address;
|
||||
|
||||
maybe_update!("min-genesis-time", min_genesis_time);
|
||||
maybe_update!("min-deposit-amount", min_deposit_amount);
|
||||
maybe_update!(
|
||||
"min-genesis-active-validator-count",
|
||||
min_genesis_active_validator_count
|
||||
);
|
||||
maybe_update!("max-effective-balance", max_effective_balance);
|
||||
maybe_update!("effective-balance-increment", effective_balance_increment);
|
||||
maybe_update!("ejection-balance", ejection_balance);
|
||||
maybe_update!("eth1-follow-distance", eth1_follow_distance);
|
||||
maybe_update!("genesis-delay", genesis_delay);
|
||||
maybe_update!("eth1-id", deposit_chain_id);
|
||||
maybe_update!("eth1-id", deposit_network_id);
|
||||
maybe_update!("seconds-per-slot", seconds_per_slot);
|
||||
maybe_update!("seconds-per-eth1-block", seconds_per_eth1_block);
|
||||
|
||||
if let Some(v) = parse_ssz_optional(matches, "genesis-fork-version")? {
|
||||
spec.genesis_fork_version = v;
|
||||
}
|
||||
|
||||
if let Some(proposer_score_boost) = parse_optional(matches, "proposer-score-boost")? {
|
||||
spec.proposer_score_boost = Some(proposer_score_boost);
|
||||
}
|
||||
|
||||
if let Some(fork_epoch) = parse_optional(matches, "altair-fork-epoch")? {
|
||||
spec.altair_fork_epoch = Some(fork_epoch);
|
||||
}
|
||||
|
||||
if let Some(fork_epoch) = parse_optional(matches, "bellatrix-fork-epoch")? {
|
||||
spec.bellatrix_fork_epoch = Some(fork_epoch);
|
||||
}
|
||||
|
||||
if let Some(fork_epoch) = parse_optional(matches, "capella-fork-epoch")? {
|
||||
spec.capella_fork_epoch = Some(fork_epoch);
|
||||
}
|
||||
|
||||
if let Some(fork_epoch) = parse_optional(matches, "deneb-fork-epoch")? {
|
||||
spec.deneb_fork_epoch = Some(fork_epoch);
|
||||
}
|
||||
|
||||
if let Some(fork_epoch) = parse_optional(matches, "electra-fork-epoch")? {
|
||||
spec.electra_fork_epoch = Some(fork_epoch);
|
||||
}
|
||||
|
||||
if let Some(ttd) = parse_optional(matches, "ttd")? {
|
||||
spec.terminal_total_difficulty = ttd;
|
||||
}
|
||||
|
||||
let validator_count = parse_required(matches, "validator-count")?;
|
||||
let execution_payload_header: Option<ExecutionPayloadHeader<E>> =
|
||||
parse_optional(matches, "execution-payload-header")?
|
||||
.map(|filename: String| {
|
||||
let mut bytes = vec![];
|
||||
let mut file = File::open(filename.as_str())
|
||||
.map_err(|e| format!("Unable to open {}: {}", filename, e))?;
|
||||
file.read_to_end(&mut bytes)
|
||||
.map_err(|e| format!("Unable to read {}: {}", filename, e))?;
|
||||
let fork_name = spec.fork_name_at_epoch(Epoch::new(0));
|
||||
match fork_name {
|
||||
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(
|
||||
"genesis fork must be post-merge".to_string(),
|
||||
)),
|
||||
ForkName::Bellatrix => {
|
||||
ExecutionPayloadHeaderBellatrix::<E>::from_ssz_bytes(bytes.as_slice())
|
||||
.map(ExecutionPayloadHeader::Bellatrix)
|
||||
}
|
||||
ForkName::Capella => {
|
||||
ExecutionPayloadHeaderCapella::<E>::from_ssz_bytes(bytes.as_slice())
|
||||
.map(ExecutionPayloadHeader::Capella)
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
ExecutionPayloadHeaderDeneb::<E>::from_ssz_bytes(bytes.as_slice())
|
||||
.map(ExecutionPayloadHeader::Deneb)
|
||||
}
|
||||
ForkName::Electra => {
|
||||
ExecutionPayloadHeaderElectra::<E>::from_ssz_bytes(bytes.as_slice())
|
||||
.map(ExecutionPayloadHeader::Electra)
|
||||
}
|
||||
}
|
||||
.map_err(|e| format!("SSZ decode failed: {:?}", e))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let (eth1_block_hash, genesis_time) = if let Some(payload) = execution_payload_header.as_ref() {
|
||||
let eth1_block_hash =
|
||||
parse_optional(matches, "eth1-block-hash")?.unwrap_or_else(|| payload.block_hash());
|
||||
let genesis_time =
|
||||
parse_optional(matches, "genesis-time")?.unwrap_or_else(|| payload.timestamp());
|
||||
(eth1_block_hash, genesis_time)
|
||||
} else {
|
||||
let eth1_block_hash = parse_required(matches, "eth1-block-hash").map_err(|_| {
|
||||
"One of `--execution-payload-header` or `--eth1-block-hash` must be set".to_string()
|
||||
})?;
|
||||
let genesis_time = parse_optional(matches, "genesis-time")?.unwrap_or(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| format!("Unable to get time: {:?}", e))?
|
||||
.as_secs(),
|
||||
);
|
||||
(eth1_block_hash, genesis_time)
|
||||
};
|
||||
|
||||
let genesis_state_bytes = if matches.get_flag("interop-genesis-state") {
|
||||
let keypairs = generate_deterministic_keypairs(validator_count);
|
||||
let keypairs: Vec<_> = keypairs.into_iter().map(|kp| (kp.clone(), kp)).collect();
|
||||
|
||||
let genesis_state = initialize_state_with_validators::<E>(
|
||||
&keypairs,
|
||||
genesis_time,
|
||||
eth1_block_hash.into_root(),
|
||||
execution_payload_header,
|
||||
&spec,
|
||||
)?;
|
||||
|
||||
Some(genesis_state.as_ssz_bytes())
|
||||
} else if matches.get_flag("derived-genesis-state") {
|
||||
let mnemonic_phrase: String = clap_utils::parse_required(matches, "mnemonic-phrase")?;
|
||||
let mnemonic = Mnemonic::from_phrase(&mnemonic_phrase, Language::English).map_err(|e| {
|
||||
format!(
|
||||
"Unable to derive mnemonic from string {:?}: {:?}",
|
||||
mnemonic_phrase, e
|
||||
)
|
||||
})?;
|
||||
let seed = Seed::new(&mnemonic, "");
|
||||
let keypairs = (0..validator_count as u32)
|
||||
.map(|index| {
|
||||
let (secret, _) =
|
||||
recover_validator_secret_from_mnemonic(seed.as_bytes(), index, KeyType::Voting)
|
||||
.unwrap();
|
||||
|
||||
let voting_keypair = keypair_from_secret(secret.as_bytes()).unwrap();
|
||||
|
||||
let (secret, _) = recover_validator_secret_from_mnemonic(
|
||||
seed.as_bytes(),
|
||||
index,
|
||||
KeyType::Withdrawal,
|
||||
)
|
||||
.unwrap();
|
||||
let withdrawal_keypair = keypair_from_secret(secret.as_bytes()).unwrap();
|
||||
(voting_keypair, withdrawal_keypair)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let genesis_state = initialize_state_with_validators::<E>(
|
||||
&keypairs,
|
||||
genesis_time,
|
||||
eth1_block_hash.into_root(),
|
||||
execution_payload_header,
|
||||
&spec,
|
||||
)?;
|
||||
Some(genesis_state.as_ssz_bytes())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let kzg_trusted_setup = if let Some(epoch) = spec.deneb_fork_epoch {
|
||||
// Only load the trusted setup if the deneb fork epoch is set
|
||||
if epoch != Epoch::max_value() {
|
||||
Some(TRUSTED_SETUP_BYTES.to_vec())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let testnet = Eth2NetworkConfig {
|
||||
deposit_contract_deploy_block,
|
||||
boot_enr: Some(vec![]),
|
||||
genesis_state_bytes: genesis_state_bytes.map(Into::into),
|
||||
genesis_state_source: GenesisStateSource::IncludedBytes,
|
||||
config: Config::from_chain_spec::<E>(&spec),
|
||||
kzg_trusted_setup,
|
||||
};
|
||||
|
||||
testnet.write_to_file(testnet_dir_path, overwrite_files)
|
||||
}
|
||||
|
||||
/// Returns a `BeaconState` with the given validator keypairs embedded into the
|
||||
/// genesis state. This allows us to start testnets without having to deposit validators
|
||||
/// manually.
|
||||
///
|
||||
/// The optional `execution_payload_header` allows us to start a network from the bellatrix
|
||||
/// fork without the need to transition to altair and bellatrix.
|
||||
///
|
||||
/// We need to ensure that `eth1_block_hash` is equal to the genesis block hash that is
|
||||
/// generated from the execution side `genesis.json`.
|
||||
fn initialize_state_with_validators<E: EthSpec>(
|
||||
keypairs: &[(Keypair, Keypair)], // Voting and Withdrawal keypairs
|
||||
genesis_time: u64,
|
||||
eth1_block_hash: Hash256,
|
||||
execution_payload_header: Option<ExecutionPayloadHeader<E>>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<E>, String> {
|
||||
// If no header is provided, then start from a Bellatrix state by default
|
||||
let default_header: ExecutionPayloadHeader<E> =
|
||||
ExecutionPayloadHeader::Bellatrix(ExecutionPayloadHeaderBellatrix {
|
||||
block_hash: ExecutionBlockHash::from_root(eth1_block_hash),
|
||||
parent_hash: ExecutionBlockHash::zero(),
|
||||
..ExecutionPayloadHeaderBellatrix::default()
|
||||
});
|
||||
let execution_payload_header = execution_payload_header.unwrap_or(default_header);
|
||||
// Empty eth1 data
|
||||
let eth1_data = Eth1Data {
|
||||
block_hash: eth1_block_hash,
|
||||
deposit_count: 0,
|
||||
deposit_root: Hash256::from_str(
|
||||
"0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e",
|
||||
)
|
||||
.unwrap(), // empty deposit tree root
|
||||
};
|
||||
let mut state = BeaconState::new(genesis_time, eth1_data, spec);
|
||||
|
||||
// Seed RANDAO with Eth1 entropy
|
||||
state.fill_randao_mixes_with(eth1_block_hash).unwrap();
|
||||
|
||||
for keypair in keypairs.iter() {
|
||||
let withdrawal_credentials = |pubkey: &PublicKey| {
|
||||
let mut credentials = hash(&pubkey.as_ssz_bytes());
|
||||
credentials[0] = spec.bls_withdrawal_prefix_byte;
|
||||
Hash256::from_slice(&credentials)
|
||||
};
|
||||
let amount = spec.max_effective_balance;
|
||||
// Create a new validator.
|
||||
let validator = Validator {
|
||||
pubkey: keypair.0.pk.clone().into(),
|
||||
withdrawal_credentials: withdrawal_credentials(&keypair.1.pk),
|
||||
activation_eligibility_epoch: spec.far_future_epoch,
|
||||
activation_epoch: spec.far_future_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
effective_balance: std::cmp::min(
|
||||
amount - amount % (spec.effective_balance_increment),
|
||||
spec.max_effective_balance,
|
||||
),
|
||||
slashed: false,
|
||||
};
|
||||
state.validators_mut().push(validator).unwrap();
|
||||
state.balances_mut().push(amount).unwrap();
|
||||
}
|
||||
|
||||
process_activations(&mut state, spec).unwrap();
|
||||
|
||||
if spec
|
||||
.altair_fork_epoch
|
||||
.map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch())
|
||||
{
|
||||
upgrade_to_altair(&mut state, spec).unwrap();
|
||||
|
||||
state.fork_mut().previous_version = spec.altair_fork_version;
|
||||
}
|
||||
|
||||
// Similarly, perform an upgrade to Bellatrix if configured from genesis.
|
||||
if spec
|
||||
.bellatrix_fork_epoch
|
||||
.map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch())
|
||||
{
|
||||
upgrade_to_bellatrix(&mut state, spec).unwrap();
|
||||
|
||||
// Remove intermediate Altair fork from `state.fork`.
|
||||
state.fork_mut().previous_version = spec.bellatrix_fork_version;
|
||||
|
||||
// Override latest execution payload header.
|
||||
// See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing
|
||||
if let ExecutionPayloadHeader::Bellatrix(ref header) = execution_payload_header {
|
||||
*state
|
||||
.latest_execution_payload_header_bellatrix_mut()
|
||||
.or(Err("mismatched fork".to_string()))? = header.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Similarly, perform an upgrade to Capella if configured from genesis.
|
||||
if spec
|
||||
.capella_fork_epoch
|
||||
.map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch())
|
||||
{
|
||||
upgrade_to_capella(&mut state, spec).unwrap();
|
||||
|
||||
// Remove intermediate Bellatrix fork from `state.fork`.
|
||||
state.fork_mut().previous_version = spec.capella_fork_version;
|
||||
|
||||
// Override latest execution payload header.
|
||||
// See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing
|
||||
if let ExecutionPayloadHeader::Capella(ref header) = execution_payload_header {
|
||||
*state
|
||||
.latest_execution_payload_header_capella_mut()
|
||||
.or(Err("mismatched fork".to_string()))? = header.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Similarly, perform an upgrade to Deneb if configured from genesis.
|
||||
if spec
|
||||
.deneb_fork_epoch
|
||||
.map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch())
|
||||
{
|
||||
upgrade_to_deneb(&mut state, spec).unwrap();
|
||||
|
||||
// Remove intermediate Capella fork from `state.fork`.
|
||||
state.fork_mut().previous_version = spec.deneb_fork_version;
|
||||
|
||||
// Override latest execution payload header.
|
||||
// See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing
|
||||
if let ExecutionPayloadHeader::Deneb(ref header) = execution_payload_header {
|
||||
*state
|
||||
.latest_execution_payload_header_deneb_mut()
|
||||
.or(Err("mismatched fork".to_string()))? = header.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Similarly, perform an upgrade to Electra if configured from genesis.
|
||||
if spec
|
||||
.electra_fork_epoch
|
||||
.map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch())
|
||||
{
|
||||
upgrade_to_electra(&mut state, spec).unwrap();
|
||||
|
||||
// Remove intermediate Deneb fork from `state.fork`.
|
||||
state.fork_mut().previous_version = spec.electra_fork_version;
|
||||
|
||||
// Override latest execution payload header.
|
||||
// See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/bellatrix/beacon-chain.md#testing
|
||||
if let ExecutionPayloadHeader::Electra(ref header) = execution_payload_header {
|
||||
*state
|
||||
.latest_execution_payload_header_electra_mut()
|
||||
.or(Err("mismatched fork".to_string()))? = header.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have our validators, initialize the caches (including the committees)
|
||||
state.build_caches(spec).unwrap();
|
||||
|
||||
// Set genesis validators root for domain separation and chain versioning
|
||||
*state.genesis_validators_root_mut() = state.update_validators_tree_hash_cache().unwrap();
|
||||
|
||||
// Sanity check for state fork matching config fork.
|
||||
state
|
||||
.fork_name(spec)
|
||||
.map_err(|e| format!("state fork mismatch: {e:?}"))?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
use account_utils::{eth2_keystore::keypair_from_secret, mnemonic_from_phrase};
|
||||
use clap::ArgMatches;
|
||||
use eth2_network_config::Eth2NetworkConfig;
|
||||
use eth2_wallet::bip39::Seed;
|
||||
use eth2_wallet::{recover_validator_secret_from_mnemonic, KeyType};
|
||||
use ssz::Encode;
|
||||
use state_processing::common::DepositDataTree;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{BeaconState, DepositData, EthSpec, Hash256, SignatureBytes, DEPOSIT_TREE_DEPTH};
|
||||
|
||||
pub fn run<E: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), String> {
|
||||
let path = matches
|
||||
.get_one::<String>("ssz-state")
|
||||
.ok_or("ssz-state not specified")?
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| format!("Unable to parse ssz-state: {}", e))?;
|
||||
|
||||
let mnemonic_phrase = matches
|
||||
.get_one::<String>("mnemonic")
|
||||
.ok_or("mnemonic not specified")?;
|
||||
|
||||
let eth2_network_config = Eth2NetworkConfig::load(testnet_dir)?;
|
||||
let spec = ð2_network_config.chain_spec::<E>()?;
|
||||
|
||||
let mut state: BeaconState<E> = {
|
||||
let mut file = File::open(&path).map_err(|e| format!("Unable to open file: {}", e))?;
|
||||
|
||||
let mut ssz = vec![];
|
||||
|
||||
file.read_to_end(&mut ssz)
|
||||
.map_err(|e| format!("Unable to read file: {}", e))?;
|
||||
|
||||
BeaconState::from_ssz_bytes(&ssz, spec)
|
||||
.map_err(|e| format!("Unable to decode SSZ: {:?}", e))?
|
||||
};
|
||||
|
||||
let mnemonic = mnemonic_from_phrase(mnemonic_phrase)?;
|
||||
let seed = Seed::new(&mnemonic, "");
|
||||
|
||||
let mut deposit_tree = DepositDataTree::create(&[], 0, DEPOSIT_TREE_DEPTH);
|
||||
let mut deposit_root = Hash256::zero();
|
||||
let validators = state.validators_mut();
|
||||
for index in 0..validators.len() {
|
||||
let (secret, _) =
|
||||
recover_validator_secret_from_mnemonic(seed.as_bytes(), index as u32, KeyType::Voting)
|
||||
.map_err(|e| format!("Unable to generate validator key: {:?}", e))?;
|
||||
|
||||
let keypair = keypair_from_secret(secret.as_bytes())
|
||||
.map_err(|e| format!("Unable build keystore: {:?}", e))?;
|
||||
|
||||
eprintln!("{}: {}", index, keypair.pk);
|
||||
|
||||
validators.get_mut(index).unwrap().pubkey = keypair.pk.into();
|
||||
|
||||
// Update the deposit tree.
|
||||
let mut deposit_data = DepositData {
|
||||
pubkey: validators.get(index).unwrap().pubkey,
|
||||
// Set this to a junk value since it's very time consuming to generate the withdrawal
|
||||
// keys and it's not useful for the time being.
|
||||
withdrawal_credentials: Hash256::zero(),
|
||||
amount: spec.min_deposit_amount,
|
||||
signature: SignatureBytes::empty(),
|
||||
};
|
||||
deposit_data.signature = deposit_data.create_signature(&keypair.sk, spec);
|
||||
deposit_tree
|
||||
.push_leaf(deposit_data.tree_hash_root())
|
||||
.map_err(|e| format!("failed to create deposit tree: {:?}", e))?;
|
||||
deposit_root = deposit_tree.root();
|
||||
}
|
||||
|
||||
// Update the genesis validators root since we changed the validators.
|
||||
*state.genesis_validators_root_mut() = state.validators().tree_hash_root();
|
||||
|
||||
// Update the deposit root with our simulated deposits.
|
||||
state.eth1_data_mut().deposit_root = deposit_root;
|
||||
|
||||
let mut file = File::create(path).map_err(|e| format!("Unable to create file: {}", e))?;
|
||||
|
||||
file.write_all(&state.as_ssz_bytes())
|
||||
.map_err(|e| format!("Unable to write to file: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "lighthouse"
|
||||
version = "5.1.3"
|
||||
version = "5.2.0"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = { workspace = true }
|
||||
autotests = false
|
||||
rust-version = "1.75.0"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[features]
|
||||
default = ["slasher-lmdb"]
|
||||
|
||||
@@ -36,7 +36,7 @@ use {futures::channel::oneshot, std::cell::RefCell};
|
||||
|
||||
pub use task_executor::test_utils::null_logger;
|
||||
|
||||
const LOG_CHANNEL_SIZE: usize = 2048;
|
||||
const LOG_CHANNEL_SIZE: usize = 16384;
|
||||
const SSE_LOG_CHANNEL_SIZE: usize = 2048;
|
||||
/// The maximum time in seconds the client will wait for all internal tasks to shutdown.
|
||||
const MAXIMUM_SHUTDOWN_TIME: u64 = 15;
|
||||
|
||||
@@ -2178,6 +2178,21 @@ fn slasher_broadcast_flag_no_default() {
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn slasher_broadcast_flag_no_argument() {
|
||||
CommandLineTest::new()
|
||||
.flag("slasher", None)
|
||||
.flag("slasher-max-db-size", Some("1"))
|
||||
.flag("slasher-broadcast", None)
|
||||
.run_with_zero_port()
|
||||
.with_config(|config| {
|
||||
let slasher_config = config
|
||||
.slasher
|
||||
.as_ref()
|
||||
.expect("Unable to parse Slasher config");
|
||||
assert!(slasher_config.broadcast);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn slasher_broadcast_flag_true() {
|
||||
CommandLineTest::new()
|
||||
.flag("slasher", None)
|
||||
|
||||
@@ -593,7 +593,7 @@ fn wrong_broadcast_flag() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latency_measurement_service() {
|
||||
fn disable_latency_measurement_service() {
|
||||
CommandLineTest::new()
|
||||
.flag("disable-latency-measurement-service", None)
|
||||
.run()
|
||||
@@ -601,6 +601,16 @@ fn latency_measurement_service() {
|
||||
assert!(!config.enable_latency_measurement_service);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn latency_measurement_service() {
|
||||
// This flag is DEPRECATED so has no effect, but should still be accepted.
|
||||
CommandLineTest::new()
|
||||
.flag("latency-measurement-service", Some("false"))
|
||||
.run()
|
||||
.with_config(|config| {
|
||||
assert!(config.enable_latency_measurement_service);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validator_registration_batch_size() {
|
||||
|
||||
1
scripts/local_testnet/.gitignore
vendored
Normal file
1
scripts/local_testnet/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
logs
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
exec anvil \
|
||||
--balance 1000000000 \
|
||||
--gas-limit 1000000000 \
|
||||
--accounts 10 \
|
||||
--mnemonic "$ETH1_NETWORK_MNEMONIC" \
|
||||
--block-time $SECONDS_PER_ETH1_BLOCK \
|
||||
--port 8545 \
|
||||
--chain-id "$CHAIN_ID"
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Starts a beacon node based upon a genesis state created by `./setup.sh`.
|
||||
#
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
SUBSCRIBE_ALL_SUBNETS=
|
||||
DEBUG_LEVEL=${DEBUG_LEVEL:-info}
|
||||
|
||||
# Get options
|
||||
while getopts "d:sh" flag; do
|
||||
case "${flag}" in
|
||||
d) DEBUG_LEVEL=${OPTARG};;
|
||||
s) SUBSCRIBE_ALL_SUBNETS="--subscribe-all-subnets";;
|
||||
h)
|
||||
echo "Start a beacon node"
|
||||
echo
|
||||
echo "usage: $0 <Options> <DATADIR> <NETWORK-PORT> <HTTP-PORT>"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -s: pass --subscribe-all-subnets to 'lighthouse bn ...', default is not passed"
|
||||
echo " -d: DEBUG_LEVEL, default info"
|
||||
echo " -h: this help"
|
||||
echo
|
||||
echo "Positional arguments:"
|
||||
echo " DATADIR Value for --datadir parameter"
|
||||
echo " NETWORK-PORT Value for --enr-udp-port, --enr-tcp-port and --port"
|
||||
echo " HTTP-PORT Value for --http-port"
|
||||
echo " EXECUTION-ENDPOINT Value for --execution-endpoint"
|
||||
echo " EXECUTION-JWT Value for --execution-jwt"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Get positional arguments
|
||||
data_dir=${@:$OPTIND+0:1}
|
||||
tcp_port=${@:$OPTIND+1:1}
|
||||
quic_port=${@:$OPTIND+2:1}
|
||||
http_port=${@:$OPTIND+3:1}
|
||||
execution_endpoint=${@:$OPTIND+4:1}
|
||||
execution_jwt=${@:$OPTIND+5:1}
|
||||
|
||||
lighthouse_binary=lighthouse
|
||||
|
||||
exec $lighthouse_binary \
|
||||
--debug-level $DEBUG_LEVEL \
|
||||
bn \
|
||||
$SUBSCRIBE_ALL_SUBNETS \
|
||||
--datadir $data_dir \
|
||||
--testnet-dir $TESTNET_DIR \
|
||||
--enable-private-discovery \
|
||||
--disable-peer-scoring \
|
||||
--staking \
|
||||
--enr-address 127.0.0.1 \
|
||||
--enr-udp-port $tcp_port \
|
||||
--enr-tcp-port $tcp_port \
|
||||
--enr-quic-port $quic_port \
|
||||
--port $tcp_port \
|
||||
--quic-port $quic_port \
|
||||
--http-port $http_port \
|
||||
--disable-packet-filter \
|
||||
--target-peers $((BN_COUNT - 1)) \
|
||||
--execution-endpoint $execution_endpoint \
|
||||
--execution-jwt $execution_jwt \
|
||||
$BN_ARGS
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Generates a bootnode enr and saves it in $TESTNET/boot_enr.yaml
|
||||
# Starts a bootnode from the generated enr.
|
||||
#
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
echo "Generating bootnode enr"
|
||||
|
||||
lcli \
|
||||
generate-bootnode-enr \
|
||||
--ip 127.0.0.1 \
|
||||
--udp-port $BOOTNODE_PORT \
|
||||
--tcp-port $BOOTNODE_PORT \
|
||||
--genesis-fork-version $GENESIS_FORK_VERSION \
|
||||
--output-dir $DATADIR/bootnode
|
||||
|
||||
bootnode_enr=`cat $DATADIR/bootnode/enr.dat`
|
||||
echo "- $bootnode_enr" > $TESTNET_DIR/boot_enr.yaml
|
||||
|
||||
echo "Generated bootnode enr and written to $TESTNET_DIR/boot_enr.yaml"
|
||||
|
||||
DEBUG_LEVEL=${1:-info}
|
||||
|
||||
echo "Starting bootnode"
|
||||
|
||||
exec lighthouse boot_node \
|
||||
--testnet-dir $TESTNET_DIR \
|
||||
--port $BOOTNODE_PORT \
|
||||
--listen-address 127.0.0.1 \
|
||||
--disable-packet-filter \
|
||||
--network-dir $DATADIR/bootnode \
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Deletes all files associated with the local testnet.
|
||||
#
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
if [ -d $DATADIR ]; then
|
||||
rm -rf $DATADIR
|
||||
fi
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Print all the logs output from local testnet
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
for f in "$TESTNET_DIR"/*.log
|
||||
do
|
||||
[[ -e "$f" ]] || break # handle the case of no *.log files
|
||||
echo "============================================================================="
|
||||
echo "$f"
|
||||
echo "============================================================================="
|
||||
cat "$f"
|
||||
echo ""
|
||||
done
|
||||
@@ -1,3 +0,0 @@
|
||||
priv_key="02fd74636e96a8ffac8e7b01b0de8dea94d6bcf4989513b38cf59eb32163ff91"
|
||||
source ./vars.env
|
||||
exec $EL_BOOTNODE_BINARY --nodekeyhex $priv_key
|
||||
File diff suppressed because one or more lines are too long
@@ -1,53 +0,0 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
# Get options
|
||||
while getopts "d:sh" flag; do
|
||||
case "${flag}" in
|
||||
d) DEBUG_LEVEL=${OPTARG};;
|
||||
s) SUBSCRIBE_ALL_SUBNETS="--subscribe-all-subnets";;
|
||||
h)
|
||||
echo "Start a geth node"
|
||||
echo
|
||||
echo "usage: $0 <Options> <DATADIR> <NETWORK-PORT> <HTTP-PORT>"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -h: this help"
|
||||
echo
|
||||
echo "Positional arguments:"
|
||||
echo " DATADIR Value for --datadir parameter"
|
||||
echo " NETWORK-PORT Value for --port"
|
||||
echo " HTTP-PORT Value for --http.port"
|
||||
echo " AUTH-PORT Value for --authrpc.port"
|
||||
echo " GENESIS_FILE Value for geth init"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Get positional arguments
|
||||
data_dir=${@:$OPTIND+0:1}
|
||||
network_port=${@:$OPTIND+1:1}
|
||||
http_port=${@:$OPTIND+2:1}
|
||||
auth_port=${@:$OPTIND+3:1}
|
||||
genesis_file=${@:$OPTIND+4:1}
|
||||
|
||||
# Init
|
||||
$GETH_BINARY init \
|
||||
--datadir $data_dir \
|
||||
$genesis_file
|
||||
|
||||
echo "Completed init"
|
||||
|
||||
exec $GETH_BINARY \
|
||||
--datadir $data_dir \
|
||||
--ipcdisable \
|
||||
--http \
|
||||
--http.api="engine,eth,web3,net,debug" \
|
||||
--networkid=$CHAIN_ID \
|
||||
--syncmode=full \
|
||||
--bootnodes $EL_BOOTNODE_ENODE \
|
||||
--port $network_port \
|
||||
--http.port $http_port \
|
||||
--authrpc.port $auth_port
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Kill processes
|
||||
|
||||
set -Euo pipefail
|
||||
|
||||
# First parameter is the file with
|
||||
# one pid per line.
|
||||
if [ -f "$1" ]; then
|
||||
while read pid
|
||||
do
|
||||
# handle the case of blank lines
|
||||
[[ -n "$pid" ]] || continue
|
||||
|
||||
echo killing $pid
|
||||
kill $pid || true
|
||||
done < $1
|
||||
fi
|
||||
|
||||
|
||||
14
scripts/local_testnet/network_params.yaml
Normal file
14
scripts/local_testnet/network_params.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# Full configuration reference [here](https://github.com/kurtosis-tech/ethereum-package?tab=readme-ov-file#configuration).
|
||||
participants:
|
||||
- el_type: geth
|
||||
el_image: ethereum/client-go:latest
|
||||
cl_type: lighthouse
|
||||
cl_image: lighthouse:local
|
||||
cl_extra_params:
|
||||
- --target-peers=3
|
||||
count: 4
|
||||
network_params:
|
||||
deneb_fork_epoch: 0
|
||||
seconds_per_slot: 3
|
||||
global_log_level: debug
|
||||
snooper_enabled: false
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Resets the beacon state genesis time to now.
|
||||
#
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
NOW=$(date +%s)
|
||||
|
||||
lcli \
|
||||
change-genesis-time \
|
||||
$TESTNET_DIR/genesis.ssz \
|
||||
$(date +%s)
|
||||
|
||||
echo "Reset genesis time to now ($NOW)"
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Produces a testnet specification and a genesis state where the genesis time
|
||||
# is now + $GENESIS_DELAY.
|
||||
#
|
||||
# Generates datadirs for multiple validator keys according to the
|
||||
# $VALIDATOR_COUNT and $BN_COUNT variables.
|
||||
#
|
||||
|
||||
set -o nounset -o errexit -o pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
|
||||
NOW=`date +%s`
|
||||
GENESIS_TIME=`expr $NOW + $GENESIS_DELAY`
|
||||
|
||||
lcli \
|
||||
new-testnet \
|
||||
--spec $SPEC_PRESET \
|
||||
--deposit-contract-address $DEPOSIT_CONTRACT_ADDRESS \
|
||||
--testnet-dir $TESTNET_DIR \
|
||||
--min-genesis-active-validator-count $GENESIS_VALIDATOR_COUNT \
|
||||
--min-genesis-time $GENESIS_TIME \
|
||||
--genesis-delay $GENESIS_DELAY \
|
||||
--genesis-fork-version $GENESIS_FORK_VERSION \
|
||||
--altair-fork-epoch $ALTAIR_FORK_EPOCH \
|
||||
--bellatrix-fork-epoch $BELLATRIX_FORK_EPOCH \
|
||||
--capella-fork-epoch $CAPELLA_FORK_EPOCH \
|
||||
--deneb-fork-epoch $DENEB_FORK_EPOCH \
|
||||
--electra-fork-epoch $ELECTRA_FORK_EPOCH \
|
||||
--ttd $TTD \
|
||||
--eth1-block-hash $ETH1_BLOCK_HASH \
|
||||
--eth1-id $CHAIN_ID \
|
||||
--eth1-follow-distance 128 \
|
||||
--seconds-per-slot $SECONDS_PER_SLOT \
|
||||
--seconds-per-eth1-block $SECONDS_PER_ETH1_BLOCK \
|
||||
--proposer-score-boost "$PROPOSER_SCORE_BOOST" \
|
||||
--validator-count $GENESIS_VALIDATOR_COUNT \
|
||||
--interop-genesis-state \
|
||||
--force
|
||||
|
||||
echo Specification and genesis.ssz generated at $TESTNET_DIR.
|
||||
echo "Generating $VALIDATOR_COUNT validators concurrently... (this may take a while)"
|
||||
|
||||
lcli \
|
||||
insecure-validators \
|
||||
--count $VALIDATOR_COUNT \
|
||||
--base-dir $DATADIR \
|
||||
--node-count $VC_COUNT
|
||||
|
||||
echo Validators generated with keystore passwords at $DATADIR.
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
# Function to output SLOT_PER_EPOCH for mainnet or minimal
|
||||
get_spec_preset_value() {
|
||||
case "$SPEC_PRESET" in
|
||||
mainnet) echo 32 ;;
|
||||
minimal) echo 8 ;;
|
||||
gnosis) echo 16 ;;
|
||||
*) echo "Unsupported preset: $SPEC_PRESET" >&2; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
SLOT_PER_EPOCH=$(get_spec_preset_value $SPEC_PRESET)
|
||||
echo "slot_per_epoch=$SLOT_PER_EPOCH"
|
||||
|
||||
genesis_file=$1
|
||||
|
||||
# Update future hardforks time in the EL genesis file based on the CL genesis time
|
||||
GENESIS_TIME=$(lcli pretty-ssz --spec $SPEC_PRESET --testnet-dir $TESTNET_DIR BeaconState $TESTNET_DIR/genesis.ssz | jq | grep -Po 'genesis_time": "\K.*\d')
|
||||
echo $GENESIS_TIME
|
||||
CAPELLA_TIME=$((GENESIS_TIME + (CAPELLA_FORK_EPOCH * $SLOT_PER_EPOCH * SECONDS_PER_SLOT)))
|
||||
echo $CAPELLA_TIME
|
||||
sed -i 's/"shanghaiTime".*$/"shanghaiTime": '"$CAPELLA_TIME"',/g' $genesis_file
|
||||
CANCUN_TIME=$((GENESIS_TIME + (DENEB_FORK_EPOCH * $SLOT_PER_EPOCH * SECONDS_PER_SLOT)))
|
||||
echo $CANCUN_TIME
|
||||
sed -i 's/"cancunTime".*$/"cancunTime": '"$CANCUN_TIME"',/g' $genesis_file
|
||||
PRAGUE_TIME=$((GENESIS_TIME + (ELECTRA_FORK_EPOCH * $SLOT_PER_EPOCH * SECONDS_PER_SLOT)))
|
||||
echo $PRAGUE_TIME
|
||||
sed -i 's/"pragueTime".*$/"pragueTime": '"$PRAGUE_TIME"',/g' $genesis_file
|
||||
cat $genesis_file
|
||||
|
||||
@@ -1,147 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
# Start all processes necessary to create a local testnet
|
||||
|
||||
# Requires `docker`, `kurtosis`, `yq`
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
ENCLAVE_NAME=local-testnet
|
||||
NETWORK_PARAMS_FILE=$SCRIPT_DIR/network_params.yaml
|
||||
|
||||
# Set a higher ulimit in case we want to import 1000s of validators.
|
||||
ulimit -n 65536
|
||||
|
||||
# VC_COUNT is defaulted in vars.env
|
||||
DEBUG_LEVEL=${DEBUG_LEVEL:-info}
|
||||
BUILDER_PROPOSALS=
|
||||
BUILD_IMAGE=true
|
||||
BUILDER_PROPOSALS=false
|
||||
CI=false
|
||||
|
||||
# Get options
|
||||
while getopts "v:d:ph" flag; do
|
||||
while getopts "e:b:n:phc" flag; do
|
||||
case "${flag}" in
|
||||
v) VC_COUNT=${OPTARG};;
|
||||
d) DEBUG_LEVEL=${OPTARG};;
|
||||
p) BUILDER_PROPOSALS="-p";;
|
||||
e) ENCLAVE_NAME=${OPTARG};;
|
||||
b) BUILD_IMAGE=${OPTARG};;
|
||||
n) NETWORK_PARAMS_FILE=${OPTARG};;
|
||||
p) BUILDER_PROPOSALS=true;;
|
||||
c) CI=true;;
|
||||
h)
|
||||
validators=$(( $VALIDATOR_COUNT / $BN_COUNT ))
|
||||
echo "Start local testnet, defaults: 1 eth1 node, $BN_COUNT beacon nodes,"
|
||||
echo "and $VC_COUNT validator clients with each vc having $validators validators."
|
||||
echo "Start a local testnet with kurtosis."
|
||||
echo
|
||||
echo "usage: $0 <Options>"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -v: VC_COUNT default: $VC_COUNT"
|
||||
echo " -d: DEBUG_LEVEL default: info"
|
||||
echo " -e: enclave name default: $ENCLAVE_NAME"
|
||||
echo " -b: whether to build Lighthouse docker image default: $BUILD_IMAGE"
|
||||
echo " -n: kurtosis network params file path default: $NETWORK_PARAMS_FILE"
|
||||
echo " -p: enable builder proposals"
|
||||
echo " -c: CI mode, run without other additional services like Grafana and Dora explorer"
|
||||
echo " -h: this help"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( $VC_COUNT > $BN_COUNT )); then
|
||||
echo "Error $VC_COUNT is too large, must be <= BN_COUNT=$BN_COUNT"
|
||||
LH_IMAGE_NAME=$(yq eval ".participants[0].cl_image" $NETWORK_PARAMS_FILE)
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "Docker is not installed. Please install Docker and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v kurtosis &> /dev/null; then
|
||||
echo "kurtosis command not found. Please install kurtosis and try again."
|
||||
exit
|
||||
fi
|
||||
|
||||
genesis_file=${@:$OPTIND+0:1}
|
||||
if ! command -v yq &> /dev/null; then
|
||||
echo "yq not found. Please install yq and try again."
|
||||
fi
|
||||
|
||||
# Init some constants
|
||||
PID_FILE=$TESTNET_DIR/PIDS.pid
|
||||
LOG_DIR=$TESTNET_DIR
|
||||
if [ "$BUILDER_PROPOSALS" = true ]; then
|
||||
yq eval '.participants[0].vc_extra_params = ["--builder-proposals"]' -i $NETWORK_PARAMS_FILE
|
||||
echo "--builder-proposals VC flag added to network_params.yaml"
|
||||
fi
|
||||
|
||||
# Stop local testnet and remove $PID_FILE
|
||||
./stop_local_testnet.sh
|
||||
if [ "$CI" = true ]; then
|
||||
# TODO: run assertoor tests
|
||||
yq eval '.additional_services = []' -i $NETWORK_PARAMS_FILE
|
||||
echo "Running without additional services (CI mode)."
|
||||
else
|
||||
yq eval '.additional_services = ["dora", "prometheus_grafana"]' -i $NETWORK_PARAMS_FILE
|
||||
echo "Additional services dora and prometheus_grafana added to network_params.yaml"
|
||||
fi
|
||||
|
||||
# Clean $DATADIR and create empty log files so the
|
||||
# user can "tail -f" right after starting this script
|
||||
# even before its done.
|
||||
./clean.sh
|
||||
mkdir -p $LOG_DIR
|
||||
for (( bn=1; bn<=$BN_COUNT; bn++ )); do
|
||||
touch $LOG_DIR/beacon_node_$bn.log
|
||||
done
|
||||
for (( el=1; el<=$BN_COUNT; el++ )); do
|
||||
touch $LOG_DIR/geth_$el.log
|
||||
done
|
||||
for (( vc=1; vc<=$VC_COUNT; vc++ )); do
|
||||
touch $LOG_DIR/validator_node_$vc.log
|
||||
done
|
||||
if [ "$BUILD_IMAGE" = true ]; then
|
||||
echo "Building Lighthouse Docker image."
|
||||
ROOT_DIR="$SCRIPT_DIR/../.."
|
||||
docker build --build-arg FEATURES=portable -f $ROOT_DIR/Dockerfile -t $LH_IMAGE_NAME $ROOT_DIR
|
||||
else
|
||||
echo "Not rebuilding Lighthouse Docker image."
|
||||
fi
|
||||
|
||||
# Sleep with a message
|
||||
sleeping() {
|
||||
echo sleeping $1
|
||||
sleep $1
|
||||
}
|
||||
# Stop local testnet
|
||||
kurtosis enclave rm -f $ENCLAVE_NAME 2>/dev/null || true
|
||||
|
||||
# Execute the command with logs saved to a file.
|
||||
#
|
||||
# First parameter is log file name
|
||||
# Second parameter is executable name
|
||||
# Remaining parameters are passed to executable
|
||||
execute_command() {
|
||||
LOG_NAME=$1
|
||||
EX_NAME=$2
|
||||
shift
|
||||
shift
|
||||
CMD="$EX_NAME $@ >> $LOG_DIR/$LOG_NAME 2>&1"
|
||||
echo "executing: $CMD"
|
||||
echo "$CMD" > "$LOG_DIR/$LOG_NAME"
|
||||
eval "$CMD &"
|
||||
}
|
||||
|
||||
# Execute the command with logs saved to a file
|
||||
# and is PID is saved to $PID_FILE.
|
||||
#
|
||||
# First parameter is log file name
|
||||
# Second parameter is executable name
|
||||
# Remaining parameters are passed to executable
|
||||
execute_command_add_PID() {
|
||||
execute_command $@
|
||||
echo "$!" >> $PID_FILE
|
||||
}
|
||||
|
||||
|
||||
# Setup data
|
||||
echo "executing: ./setup.sh >> $LOG_DIR/setup.log"
|
||||
./setup.sh >> $LOG_DIR/setup.log 2>&1
|
||||
|
||||
# Call setup_time.sh to update future hardforks time in the EL genesis file based on the CL genesis time
|
||||
./setup_time.sh $genesis_file
|
||||
|
||||
# Delay to let boot_enr.yaml to be created
|
||||
execute_command_add_PID bootnode.log ./bootnode.sh
|
||||
sleeping 3
|
||||
|
||||
execute_command_add_PID el_bootnode.log ./el_bootnode.sh
|
||||
sleeping 3
|
||||
|
||||
# Start beacon nodes
|
||||
BN_udp_tcp_base=9000
|
||||
BN_http_port_base=8000
|
||||
|
||||
EL_base_network=7000
|
||||
EL_base_http=6000
|
||||
EL_base_auth_http=5000
|
||||
|
||||
(( $VC_COUNT < $BN_COUNT )) && SAS=-s || SAS=
|
||||
|
||||
for (( el=1; el<=$BN_COUNT; el++ )); do
|
||||
execute_command_add_PID geth_$el.log ./geth.sh $DATADIR/geth_datadir$el $((EL_base_network + $el)) $((EL_base_http + $el)) $((EL_base_auth_http + $el)) $genesis_file
|
||||
done
|
||||
|
||||
sleeping 20
|
||||
|
||||
# Reset the `genesis.json` config file fork times.
|
||||
sed -i 's/"shanghaiTime".*$/"shanghaiTime": 0,/g' $genesis_file
|
||||
sed -i 's/"cancunTime".*$/"cancunTime": 0,/g' $genesis_file
|
||||
sed -i 's/"pragueTime".*$/"pragueTime": 0,/g' $genesis_file
|
||||
|
||||
for (( bn=1; bn<=$BN_COUNT; bn++ )); do
|
||||
secret=$DATADIR/geth_datadir$bn/geth/jwtsecret
|
||||
echo $secret
|
||||
execute_command_add_PID beacon_node_$bn.log ./beacon_node.sh $SAS -d $DEBUG_LEVEL $DATADIR/node_$bn $((BN_udp_tcp_base + $bn)) $((BN_udp_tcp_base + $bn + 100)) $((BN_http_port_base + $bn)) http://localhost:$((EL_base_auth_http + $bn)) $secret
|
||||
done
|
||||
|
||||
# Start requested number of validator clients
|
||||
for (( vc=1; vc<=$VC_COUNT; vc++ )); do
|
||||
execute_command_add_PID validator_node_$vc.log ./validator_client.sh $BUILDER_PROPOSALS -d $DEBUG_LEVEL $DATADIR/node_$vc http://localhost:$((BN_http_port_base + $vc))
|
||||
done
|
||||
kurtosis run --enclave $ENCLAVE_NAME github.com/kurtosis-tech/ethereum-package --args-file $NETWORK_PARAMS_FILE
|
||||
|
||||
echo "Started!"
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# Stop all processes that were started with start_local_testnet.sh
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
ENCLAVE_NAME=${1:-local-testnet}
|
||||
LOGS_PATH=$SCRIPT_DIR/logs
|
||||
LOGS_SUBDIR=$LOGS_PATH/$ENCLAVE_NAME
|
||||
|
||||
PID_FILE=$TESTNET_DIR/PIDS.pid
|
||||
./kill_processes.sh $PID_FILE
|
||||
rm -f $PID_FILE
|
||||
# Delete existing logs directory and make sure parent directory exists.
|
||||
rm -rf $LOGS_SUBDIR && mkdir -p $LOGS_PATH
|
||||
kurtosis enclave dump $ENCLAVE_NAME $LOGS_SUBDIR
|
||||
echo "Local testnet logs stored to $LOGS_SUBDIR."
|
||||
|
||||
kurtosis enclave rm -f $ENCLAVE_NAME
|
||||
echo "Local testnet stopped."
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Starts a validator client based upon a genesis state created by
|
||||
# `./setup.sh`.
|
||||
#
|
||||
# Usage: ./validator_client.sh <DATADIR> <BEACON-NODE-HTTP> <OPTIONAL-DEBUG-LEVEL>
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
source ./vars.env
|
||||
|
||||
DEBUG_LEVEL=info
|
||||
|
||||
BUILDER_PROPOSALS=
|
||||
|
||||
# Get options
|
||||
while getopts "pd:" flag; do
|
||||
case "${flag}" in
|
||||
p) BUILDER_PROPOSALS="--builder-proposals";;
|
||||
d) DEBUG_LEVEL=${OPTARG};;
|
||||
esac
|
||||
done
|
||||
|
||||
exec lighthouse \
|
||||
--debug-level $DEBUG_LEVEL \
|
||||
vc \
|
||||
$BUILDER_PROPOSALS \
|
||||
--datadir ${@:$OPTIND:1} \
|
||||
--testnet-dir $TESTNET_DIR \
|
||||
--init-slashing-protection \
|
||||
--beacon-nodes ${@:$OPTIND+1:1} \
|
||||
--suggested-fee-recipient 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990 \
|
||||
$VC_ARGS
|
||||
@@ -1,69 +0,0 @@
|
||||
# Path to the geth binary
|
||||
GETH_BINARY=geth
|
||||
EL_BOOTNODE_BINARY=bootnode
|
||||
|
||||
# Base directories for the validator keys and secrets
|
||||
DATADIR=~/.lighthouse/local-testnet
|
||||
|
||||
# Directory for the eth2 config
|
||||
TESTNET_DIR=$DATADIR/testnet
|
||||
|
||||
# Mnemonic for generating validator keys
|
||||
MNEMONIC_PHRASE="vast thought differ pull jewel broom cook wrist tribe word before omit"
|
||||
|
||||
EL_BOOTNODE_ENODE="enode://51ea9bb34d31efc3491a842ed13b8cab70e753af108526b57916d716978b380ed713f4336a80cdb85ec2a115d5a8c0ae9f3247bed3c84d3cb025c6bab311062c@127.0.0.1:0?discport=30301"
|
||||
|
||||
# Hardcoded deposit contract
|
||||
DEPOSIT_CONTRACT_ADDRESS=4242424242424242424242424242424242424242
|
||||
|
||||
GENESIS_FORK_VERSION=0x42424242
|
||||
|
||||
# Block hash generated from genesis.json in directory
|
||||
ETH1_BLOCK_HASH=4b0e17cf5c04616d64526d292b80a1f2720cf2195d990006e4ea6950c5bbcb9f
|
||||
|
||||
VALIDATOR_COUNT=80
|
||||
GENESIS_VALIDATOR_COUNT=80
|
||||
|
||||
# Number of beacon_node instances that you intend to run
|
||||
BN_COUNT=4
|
||||
|
||||
# Number of validator clients
|
||||
VC_COUNT=$BN_COUNT
|
||||
|
||||
# Number of seconds to delay to start genesis block.
|
||||
# If started by a script this can be 0, if starting by hand
|
||||
# use something like 180.
|
||||
GENESIS_DELAY=0
|
||||
|
||||
# Port for P2P communication with bootnode
|
||||
BOOTNODE_PORT=4242
|
||||
|
||||
# Network ID and Chain ID of local eth1 test network
|
||||
CHAIN_ID=4242
|
||||
|
||||
# Hard fork configuration
|
||||
ALTAIR_FORK_EPOCH=0
|
||||
BELLATRIX_FORK_EPOCH=0
|
||||
CAPELLA_FORK_EPOCH=0
|
||||
DENEB_FORK_EPOCH=1
|
||||
ELECTRA_FORK_EPOCH=9999999
|
||||
|
||||
TTD=0
|
||||
|
||||
# Spec version (mainnet or minimal)
|
||||
SPEC_PRESET=mainnet
|
||||
|
||||
# Seconds per Eth2 slot
|
||||
SECONDS_PER_SLOT=3
|
||||
|
||||
# Seconds per Eth1 block
|
||||
SECONDS_PER_ETH1_BLOCK=3
|
||||
|
||||
# Proposer score boost percentage
|
||||
PROPOSER_SCORE_BOOST=40
|
||||
|
||||
# Command line arguments for beacon node client
|
||||
BN_ARGS=""
|
||||
|
||||
# Command line arguments for validator client
|
||||
VC_ARGS=""
|
||||
@@ -1,101 +1,129 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Requires `lighthouse`, `lcli`, `geth`, `bootnode`, `curl`, `jq`
|
||||
# Requires `docker`, `kurtosis`, `yq`, `curl`, `jq`
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
NETWORK_PARAMS_FILE=$SCRIPT_DIR/network_params.yaml
|
||||
BEHAVIOR=$1
|
||||
ENCLAVE_NAME=local-testnet-$BEHAVIOR
|
||||
|
||||
SECONDS_PER_SLOT=$(yq eval ".network_params.seconds_per_slot" $NETWORK_PARAMS_FILE)
|
||||
KEYS_PER_NODE=$(yq eval ".network_params.num_validator_keys_per_node" $NETWORK_PARAMS_FILE)
|
||||
LH_IMAGE_NAME=$(yq eval ".participants[0].cl_image" $NETWORK_PARAMS_FILE)
|
||||
|
||||
if [[ "$BEHAVIOR" != "success" ]] && [[ "$BEHAVIOR" != "failure" ]]; then
|
||||
echo "Usage: doppelganger_protection.sh [success|failure]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit_if_fails() {
|
||||
echo $@
|
||||
$@
|
||||
EXIT_CODE=$?
|
||||
if [[ $EXIT_CODE -eq 1 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
function exit_and_dump_logs() {
|
||||
local exit_code=$1
|
||||
echo "Shutting down..."
|
||||
$SCRIPT_DIR/../local_testnet/stop_local_testnet.sh $ENCLAVE_NAME
|
||||
echo "Test completed with exit code $exit_code."
|
||||
exit $exit_code
|
||||
}
|
||||
genesis_file=$2
|
||||
|
||||
source ./vars.env
|
||||
function get_service_status() {
|
||||
local service_name=$1
|
||||
kurtosis service inspect $ENCLAVE_NAME $service_name | grep Status | cut -d':' -f2 | xargs
|
||||
}
|
||||
|
||||
exit_if_fails ../local_testnet/clean.sh
|
||||
function run_command_without_exit() {
|
||||
local command=$1
|
||||
set +e
|
||||
eval "$command"
|
||||
local exit_code=$?
|
||||
set -e
|
||||
echo $exit_code
|
||||
}
|
||||
|
||||
# Start local testnet
|
||||
$SCRIPT_DIR/../local_testnet/start_local_testnet.sh -e $ENCLAVE_NAME -b false -c -n $NETWORK_PARAMS_FILE
|
||||
|
||||
echo "Setting up local testnet"
|
||||
# Immediately stop node 4 (as we only need the node 4 validator keys generated for later use)
|
||||
kurtosis service stop $ENCLAVE_NAME cl-4-lighthouse-geth el-4-geth-lighthouse vc-4-geth-lighthouse > /dev/null
|
||||
|
||||
exit_if_fails ../local_testnet/setup.sh
|
||||
# Get the http port to get the config
|
||||
BN1_HTTP_ADDRESS=`kurtosis port print $ENCLAVE_NAME cl-1-lighthouse-geth http`
|
||||
|
||||
# Duplicate this directory so slashing protection doesn't keep us from re-using validator keys
|
||||
exit_if_fails cp -R $HOME/.lighthouse/local-testnet/node_1 $HOME/.lighthouse/local-testnet/node_1_doppelganger
|
||||
# Get the genesis time and genesis delay
|
||||
MIN_GENESIS_TIME=`curl -s $BN1_HTTP_ADDRESS/eth/v1/config/spec | jq '.data.MIN_GENESIS_TIME|tonumber'`
|
||||
GENESIS_DELAY=`curl -s $BN1_HTTP_ADDRESS/eth/v1/config/spec | jq '.data.GENESIS_DELAY|tonumber'`
|
||||
|
||||
echo "Starting bootnode"
|
||||
CURRENT_TIME=`date +%s`
|
||||
# Note: doppelganger protection can only be started post epoch 0
|
||||
echo "Waiting until next epoch before starting the next validator client..."
|
||||
DELAY=$(( $SECONDS_PER_SLOT * 32 + $GENESIS_DELAY + $MIN_GENESIS_TIME - $CURRENT_TIME))
|
||||
sleep $DELAY
|
||||
|
||||
exit_if_fails ../local_testnet/bootnode.sh &> /dev/null &
|
||||
|
||||
exit_if_fails ../local_testnet/el_bootnode.sh &> /dev/null &
|
||||
|
||||
# wait for the bootnode to start
|
||||
sleep 10
|
||||
|
||||
echo "Starting local execution nodes"
|
||||
|
||||
exit_if_fails ../local_testnet/geth.sh $HOME/.lighthouse/local-testnet/geth_datadir1 6000 5000 4000 $genesis_file &> geth.log &
|
||||
exit_if_fails ../local_testnet/geth.sh $HOME/.lighthouse/local-testnet/geth_datadir2 6100 5100 4100 $genesis_file &> /dev/null &
|
||||
exit_if_fails ../local_testnet/geth.sh $HOME/.lighthouse/local-testnet/geth_datadir3 6200 5200 4200 $genesis_file &> /dev/null &
|
||||
|
||||
sleep 20
|
||||
|
||||
exit_if_fails ../local_testnet/beacon_node.sh -d debug $HOME/.lighthouse/local-testnet/node_1 8000 7000 9000 http://localhost:4000 $HOME/.lighthouse/local-testnet/geth_datadir1/geth/jwtsecret &> /dev/null &
|
||||
exit_if_fails ../local_testnet/beacon_node.sh $HOME/.lighthouse/local-testnet/node_2 8100 7100 9100 http://localhost:4100 $HOME/.lighthouse/local-testnet/geth_datadir2/geth/jwtsecret &> /dev/null &
|
||||
exit_if_fails ../local_testnet/beacon_node.sh $HOME/.lighthouse/local-testnet/node_3 8200 7200 9200 http://localhost:4200 $HOME/.lighthouse/local-testnet/geth_datadir3/geth/jwtsecret &> /dev/null &
|
||||
|
||||
echo "Starting local validator clients"
|
||||
|
||||
exit_if_fails ../local_testnet/validator_client.sh $HOME/.lighthouse/local-testnet/node_1 http://localhost:9000 &> /dev/null &
|
||||
exit_if_fails ../local_testnet/validator_client.sh $HOME/.lighthouse/local-testnet/node_2 http://localhost:9100 &> /dev/null &
|
||||
exit_if_fails ../local_testnet/validator_client.sh $HOME/.lighthouse/local-testnet/node_3 http://localhost:9200 &> /dev/null &
|
||||
|
||||
echo "Waiting an epoch before starting the next validator client"
|
||||
sleep $(( $SECONDS_PER_SLOT * 32 ))
|
||||
# Use BN2 for the next validator client
|
||||
bn_2_url=$(kurtosis service inspect $ENCLAVE_NAME cl-2-lighthouse-geth | grep 'enr-address' | cut -d'=' -f2)
|
||||
bn_2_port=4000
|
||||
|
||||
if [[ "$BEHAVIOR" == "failure" ]]; then
|
||||
|
||||
echo "Starting the doppelganger validator client"
|
||||
echo "Starting the doppelganger validator client."
|
||||
|
||||
# Use same keys as keys from VC1 and connect to BN2
|
||||
# This process should not last longer than 2 epochs
|
||||
timeout $(( $SECONDS_PER_SLOT * 32 * 2 )) ../local_testnet/validator_client.sh $HOME/.lighthouse/local-testnet/node_1_doppelganger http://localhost:9100
|
||||
DOPPELGANGER_EXIT=$?
|
||||
vc_1_range_start=0
|
||||
vc_1_range_end=$(($KEYS_PER_NODE - 1))
|
||||
vc_1_keys_artifact_id="1-lighthouse-geth-$vc_1_range_start-$vc_1_range_end-0"
|
||||
service_name=vc-1-doppelganger
|
||||
|
||||
echo "Shutting down"
|
||||
kurtosis service add \
|
||||
--files /validator_keys:$vc_1_keys_artifact_id,/testnet:el_cl_genesis_data \
|
||||
$ENCLAVE_NAME $service_name $LH_IMAGE_NAME -- lighthouse \
|
||||
vc \
|
||||
--debug-level debug \
|
||||
--testnet-dir=/testnet \
|
||||
--validators-dir=/validator_keys/keys \
|
||||
--secrets-dir=/validator_keys/secrets \
|
||||
--init-slashing-protection \
|
||||
--beacon-nodes=http://$bn_2_url:$bn_2_port \
|
||||
--enable-doppelganger-protection \
|
||||
--suggested-fee-recipient 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990
|
||||
|
||||
# Cleanup
|
||||
killall geth
|
||||
killall lighthouse
|
||||
killall bootnode
|
||||
# Check if doppelganger VC has stopped and exited. Exit code 1 means the check timed out and VC is still running.
|
||||
check_exit_cmd="until [ \$(get_service_status $service_name) != 'RUNNING' ]; do sleep 1; done"
|
||||
doppelganger_exit=$(run_command_without_exit "timeout $(( $SECONDS_PER_SLOT * 32 * 2 )) bash -c \"$check_exit_cmd\"")
|
||||
|
||||
echo "Done"
|
||||
|
||||
# We expect to find a doppelganger, exit with success error code if doppelganger was found
|
||||
# and failure if no doppelganger was found.
|
||||
if [[ $DOPPELGANGER_EXIT -eq 1 ]]; then
|
||||
exit 0
|
||||
if [[ $doppelganger_exit -eq 1 ]]; then
|
||||
echo "Test failed: expected doppelganger but VC is still running. Check the logs for details."
|
||||
exit_and_dump_logs 1
|
||||
else
|
||||
exit 1
|
||||
echo "Test passed: doppelganger found and VC process stopped successfully."
|
||||
exit_and_dump_logs 0
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "$BEHAVIOR" == "success" ]]; then
|
||||
|
||||
echo "Starting the last validator client"
|
||||
echo "Starting the last validator client."
|
||||
|
||||
../local_testnet/validator_client.sh $HOME/.lighthouse/local-testnet/node_4 http://localhost:9100 &
|
||||
DOPPELGANGER_FAILURE=0
|
||||
vc_4_range_start=$(($KEYS_PER_NODE * 3))
|
||||
vc_4_range_end=$(($KEYS_PER_NODE * 4 - 1))
|
||||
vc_4_keys_artifact_id="4-lighthouse-geth-$vc_4_range_start-$vc_4_range_end-0"
|
||||
service_name=vc-4
|
||||
|
||||
kurtosis service add \
|
||||
--files /validator_keys:$vc_4_keys_artifact_id,/testnet:el_cl_genesis_data \
|
||||
$ENCLAVE_NAME $service_name $LH_IMAGE_NAME -- lighthouse \
|
||||
vc \
|
||||
--debug-level debug \
|
||||
--testnet-dir=/testnet \
|
||||
--validators-dir=/validator_keys/keys \
|
||||
--secrets-dir=/validator_keys/secrets \
|
||||
--init-slashing-protection \
|
||||
--beacon-nodes=http://$bn_2_url:$bn_2_port \
|
||||
--enable-doppelganger-protection \
|
||||
--suggested-fee-recipient 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990
|
||||
|
||||
doppelganger_failure=0
|
||||
|
||||
# Sleep three epochs, then make sure all validators were active in epoch 2. Use
|
||||
# `is_previous_epoch_target_attester` from epoch 3 for a complete view of epoch 2 inclusion.
|
||||
@@ -104,20 +132,27 @@ if [[ "$BEHAVIOR" == "success" ]]; then
|
||||
echo "Waiting three epochs..."
|
||||
sleep $(( $SECONDS_PER_SLOT * 32 * 3 ))
|
||||
|
||||
PREVIOUS_DIR=$(pwd)
|
||||
cd $HOME/.lighthouse/local-testnet/node_4/validators
|
||||
# Get VC4 validator keys
|
||||
keys_path=$SCRIPT_DIR/$ENCLAVE_NAME/node_4/validators
|
||||
rm -rf $keys_path && mkdir -p $keys_path
|
||||
kurtosis files download $ENCLAVE_NAME $vc_4_keys_artifact_id $keys_path
|
||||
cd $keys_path/keys
|
||||
|
||||
for val in 0x*; do
|
||||
[[ -e $val ]] || continue
|
||||
curl -s localhost:9100/lighthouse/validator_inclusion/3/$val | jq | grep -q '"is_previous_epoch_target_attester": false'
|
||||
IS_ATTESTER=$?
|
||||
if [[ $IS_ATTESTER -eq 0 ]]; then
|
||||
is_attester=$(run_command_without_exit "curl -s $BN1_HTTP_ADDRESS/lighthouse/validator_inclusion/3/$val | jq | grep -q '\"is_previous_epoch_target_attester\": false'")
|
||||
if [[ $is_attester -eq 0 ]]; then
|
||||
echo "$val did not attest in epoch 2."
|
||||
else
|
||||
echo "ERROR! $val did attest in epoch 2."
|
||||
DOPPELGANGER_FAILURE=1
|
||||
doppelganger_failure=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $doppelganger_failure -eq 1 ]]; then
|
||||
exit_and_dump_logs 1
|
||||
fi
|
||||
|
||||
# Sleep two epochs, then make sure all validators were active in epoch 4. Use
|
||||
# `is_previous_epoch_target_attester` from epoch 5 for a complete view of epoch 4 inclusion.
|
||||
#
|
||||
@@ -126,30 +161,18 @@ if [[ "$BEHAVIOR" == "success" ]]; then
|
||||
sleep $(( $SECONDS_PER_SLOT * 32 * 2 ))
|
||||
for val in 0x*; do
|
||||
[[ -e $val ]] || continue
|
||||
curl -s localhost:9100/lighthouse/validator_inclusion/5/$val | jq | grep -q '"is_previous_epoch_target_attester": true'
|
||||
IS_ATTESTER=$?
|
||||
if [[ $IS_ATTESTER -eq 0 ]]; then
|
||||
is_attester=$(run_command_without_exit "curl -s $BN1_HTTP_ADDRESS/lighthouse/validator_inclusion/5/$val | jq | grep -q '\"is_previous_epoch_target_attester\": true'")
|
||||
if [[ $is_attester -eq 0 ]]; then
|
||||
echo "$val attested in epoch 4."
|
||||
else
|
||||
echo "ERROR! $val did not attest in epoch 4."
|
||||
DOPPELGANGER_FAILURE=1
|
||||
doppelganger_failure=1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Shutting down"
|
||||
|
||||
# Cleanup
|
||||
cd $PREVIOUS_DIR
|
||||
|
||||
killall geth
|
||||
killall lighthouse
|
||||
killall bootnode
|
||||
|
||||
echo "Done"
|
||||
|
||||
if [[ $DOPPELGANGER_FAILURE -eq 1 ]]; then
|
||||
exit 1
|
||||
if [[ $doppelganger_failure -eq 1 ]]; then
|
||||
exit_and_dump_logs 1
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
exit_and_dump_logs 0
|
||||
|
||||
File diff suppressed because one or more lines are too long
16
scripts/tests/network_params.yaml
Normal file
16
scripts/tests/network_params.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Full configuration reference [here](https://github.com/kurtosis-tech/ethereum-package?tab=readme-ov-file#configuration).
|
||||
participants:
|
||||
- el_type: geth
|
||||
el_image: ethereum/client-go:latest
|
||||
cl_type: lighthouse
|
||||
cl_image: lighthouse:local
|
||||
cl_extra_params:
|
||||
- --target-peers=3
|
||||
count: 4
|
||||
network_params:
|
||||
deneb_fork_epoch: 0
|
||||
seconds_per_slot: 3
|
||||
num_validator_keys_per_node: 20
|
||||
global_log_level: debug
|
||||
snooper_enabled: false
|
||||
additional_services: []
|
||||
@@ -1,66 +0,0 @@
|
||||
# Path to the geth binary
|
||||
GETH_BINARY=geth
|
||||
EL_BOOTNODE_BINARY=bootnode
|
||||
|
||||
# Base directories for the validator keys and secrets
|
||||
DATADIR=~/.lighthouse/local-testnet
|
||||
|
||||
# Directory for the eth2 config
|
||||
TESTNET_DIR=$DATADIR/testnet
|
||||
|
||||
EL_BOOTNODE_ENODE="enode://51ea9bb34d31efc3491a842ed13b8cab70e753af108526b57916d716978b380ed713f4336a80cdb85ec2a115d5a8c0ae9f3247bed3c84d3cb025c6bab311062c@127.0.0.1:0?discport=30301"
|
||||
|
||||
# Hardcoded deposit contract
|
||||
DEPOSIT_CONTRACT_ADDRESS=4242424242424242424242424242424242424242
|
||||
|
||||
GENESIS_FORK_VERSION=0x42424242
|
||||
|
||||
# Block hash generated from genesis.json in directory
|
||||
ETH1_BLOCK_HASH=7a5c656343c3a66dcf75415958b500e8873f9dab0cd588e6cf0785b52a06dd34
|
||||
|
||||
VALIDATOR_COUNT=80
|
||||
GENESIS_VALIDATOR_COUNT=80
|
||||
|
||||
# Number of beacon_node instances that you intend to run
|
||||
BN_COUNT=4
|
||||
|
||||
# Number of validator clients
|
||||
VC_COUNT=$BN_COUNT
|
||||
|
||||
# Number of seconds to delay to start genesis block.
|
||||
# If started by a script this can be 0, if starting by hand
|
||||
# use something like 180.
|
||||
GENESIS_DELAY=0
|
||||
|
||||
# Port for P2P communication with bootnode
|
||||
BOOTNODE_PORT=4242
|
||||
|
||||
# Network ID and Chain ID of local eth1 test network
|
||||
CHAIN_ID=4242
|
||||
|
||||
# Hard fork configuration
|
||||
ALTAIR_FORK_EPOCH=0
|
||||
BELLATRIX_FORK_EPOCH=0
|
||||
CAPELLA_FORK_EPOCH=0
|
||||
DENEB_FORK_EPOCH=0
|
||||
ELECTRA_FORK_EPOCH=18446744073709551615
|
||||
|
||||
TTD=0
|
||||
|
||||
# Spec version (mainnet or minimal)
|
||||
SPEC_PRESET=mainnet
|
||||
|
||||
# Seconds per Eth2 slot
|
||||
SECONDS_PER_SLOT=3
|
||||
|
||||
# Seconds per Eth1 block
|
||||
SECONDS_PER_ETH1_BLOCK=1
|
||||
|
||||
# Proposer score boost percentage
|
||||
PROPOSER_SCORE_BOOST=70
|
||||
|
||||
# Command line arguments for beacon node client
|
||||
BN_ARGS=""
|
||||
|
||||
# Enable doppelganger detection
|
||||
VC_ARGS=" --enable-doppelganger-protection "
|
||||
@@ -95,7 +95,7 @@ impl DepositContract {
|
||||
.await
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to deploy contract: {}. Is scripts/anvil_tests_node.sh running?.",
|
||||
"Failed to deploy contract: {}. Is the RPC server running?.",
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -7,7 +7,7 @@ use types::{Epoch, Hash256, PublicKeyBytes, Slot};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct InterchangeMetadata {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub interchange_format_version: u64,
|
||||
@@ -16,7 +16,7 @@ pub struct InterchangeMetadata {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct InterchangeData {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub signed_blocks: Vec<SignedBlock>,
|
||||
@@ -25,7 +25,7 @@ pub struct InterchangeData {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct SignedBlock {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub slot: Slot,
|
||||
@@ -35,7 +35,7 @@ pub struct SignedBlock {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct SignedAttestation {
|
||||
#[serde(with = "serde_utils::quoted_u64::require_quotes")]
|
||||
pub source_epoch: Epoch,
|
||||
@@ -46,7 +46,7 @@ pub struct SignedAttestation {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct Interchange {
|
||||
pub metadata: InterchangeMetadata,
|
||||
pub data: Vec<InterchangeData>,
|
||||
|
||||
@@ -9,7 +9,7 @@ use tempfile::tempdir;
|
||||
use types::{Epoch, Hash256, PublicKeyBytes, Slot};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct MultiTestCase {
|
||||
pub name: String,
|
||||
pub genesis_validators_root: Hash256,
|
||||
@@ -17,7 +17,7 @@ pub struct MultiTestCase {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct TestCase {
|
||||
pub should_succeed: bool,
|
||||
pub contains_slashable_data: bool,
|
||||
@@ -27,7 +27,7 @@ pub struct TestCase {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct TestBlock {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub slot: Slot,
|
||||
@@ -37,7 +37,7 @@ pub struct TestBlock {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||
pub struct TestAttestation {
|
||||
pub pubkey: PublicKeyBytes,
|
||||
pub source_epoch: Epoch,
|
||||
|
||||
@@ -406,6 +406,15 @@ pub fn cli_app() -> Command {
|
||||
.help_heading(FLAG_HEADER)
|
||||
.display_order(0)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("latency-measurement-service")
|
||||
.long("latency-measurement-service")
|
||||
.help("DEPRECATED")
|
||||
.action(ArgAction::Set)
|
||||
.help_heading(FLAG_HEADER)
|
||||
.display_order(0)
|
||||
.hide(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("validator-registration-batch-size")
|
||||
.long("validator-registration-batch-size")
|
||||
|
||||
@@ -412,6 +412,17 @@ impl Config {
|
||||
config.enable_latency_measurement_service =
|
||||
!cli_args.get_flag("disable-latency-measurement-service");
|
||||
|
||||
if cli_args
|
||||
.get_one::<String>("latency-measurement-service")
|
||||
.is_some()
|
||||
{
|
||||
warn!(
|
||||
log,
|
||||
"latency-measurement-service flag";
|
||||
"note" => "deprecated flag has no effect and should be removed"
|
||||
);
|
||||
}
|
||||
|
||||
config.validator_registration_batch_size =
|
||||
parse_required(cli_args, "validator-registration-batch-size")?;
|
||||
if config.validator_registration_batch_size == 0 {
|
||||
|
||||
@@ -40,7 +40,7 @@ impl DumpConfig {
|
||||
|
||||
pub fn cli_app() -> Command {
|
||||
Command::new(CMD)
|
||||
.visible_aliases(["vm", "validator-manager", CMD])
|
||||
.visible_aliases(["vm", "validator-manager"])
|
||||
.display_order(0)
|
||||
.styles(get_color_style())
|
||||
.about("Utilities for managing a Lighthouse validator client via the HTTP API.")
|
||||
|
||||
Reference in New Issue
Block a user