diff --git a/Cargo.lock b/Cargo.lock index 71d7a96ffc..db12b6e7c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3028,6 +3028,7 @@ dependencies = [ "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", "types 0.1.0", ] diff --git a/beacon_node/beacon_chain/src/fork_choice.rs b/beacon_node/beacon_chain/src/fork_choice.rs index 43bb588963..fde5585af2 100644 --- a/beacon_node/beacon_chain/src/fork_choice.rs +++ b/beacon_node/beacon_chain/src/fork_choice.rs @@ -3,13 +3,19 @@ use parking_lot::RwLock; use proto_array_fork_choice::ProtoArrayForkChoice; use ssz_derive::{Decode, Encode}; use state_processing::common::get_attesting_indices; +use std::fs::File; +use std::io::Write; use std::marker::PhantomData; +use std::time::{SystemTime, UNIX_EPOCH}; use store::Error as StoreError; use types::{ Attestation, BeaconBlock, BeaconState, BeaconStateError, Checkpoint, Epoch, EthSpec, Hash256, Slot, }; +/// If `true`, fork choice will be dumped to a JSON file in `/tmp` whenever find head fail. +pub const FORK_CHOICE_DEBUGGING: bool = true; + type Result = std::result::Result; #[derive(Debug, PartialEq)] @@ -250,6 +256,20 @@ impl ForkChoice { metrics::stop_timer(timer); + if FORK_CHOICE_DEBUGGING { + if let Err(e) = &result { + if let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) { + let time = duration.as_millis(); + if let Ok(mut file) = File::create(format!("/tmp/fork-choice-{}", time)) { + let _ = write!(file, "{:?}\n", e); + if let Ok(json) = self.backend.as_json() { + let _ = write!(file, "{}", json); + } + } + } + } + } + result } diff --git a/eth2/proto_array_fork_choice/Cargo.toml b/eth2/proto_array_fork_choice/Cargo.toml index 687b6aefcc..14995a2172 100644 --- a/eth2/proto_array_fork_choice/Cargo.toml +++ b/eth2/proto_array_fork_choice/Cargo.toml @@ -17,3 +17,4 @@ eth2_ssz_derive = "0.1.0" serde = "1.0.102" serde_derive = "1.0.102" serde_yaml = "0.8.11" +serde_json = "1.0" diff --git a/eth2/proto_array_fork_choice/src/proto_array.rs b/eth2/proto_array_fork_choice/src/proto_array.rs index 9bfa6abeb2..facd602144 100644 --- a/eth2/proto_array_fork_choice/src/proto_array.rs +++ b/eth2/proto_array_fork_choice/src/proto_array.rs @@ -1,9 +1,10 @@ use crate::error::Error; +use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use std::collections::HashMap; use types::{Epoch, Hash256, Slot}; -#[derive(Clone, PartialEq, Debug, Encode, Decode)] +#[derive(Clone, PartialEq, Debug, Encode, Decode, Serialize, Deserialize)] pub struct ProtoNode { /// The `slot` is not necessary for `ProtoArray`, it just exists so external components can /// easily query the block slot. This is useful for upstream fork choice logic. @@ -17,7 +18,7 @@ pub struct ProtoNode { best_descendant: Option, } -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize)] pub struct ProtoArray { /// Do not attempt to prune the tree unless it has at least this many nodes. Small prunes /// simply waste time. diff --git a/eth2/proto_array_fork_choice/src/proto_array_fork_choice.rs b/eth2/proto_array_fork_choice/src/proto_array_fork_choice.rs index 1fc8dfaa7a..b3de443f31 100644 --- a/eth2/proto_array_fork_choice/src/proto_array_fork_choice.rs +++ b/eth2/proto_array_fork_choice/src/proto_array_fork_choice.rs @@ -2,6 +2,7 @@ use crate::error::Error; use crate::proto_array::ProtoArray; use crate::ssz_container::SszContainer; use parking_lot::RwLock; +use serde_json; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; use std::collections::HashMap; @@ -220,6 +221,11 @@ impl ProtoArrayForkChoice { .map(Into::into) .map_err(|e| format!("Failed to decode ProtoArrayForkChoice: {:?}", e)) } + + pub fn as_json(&self) -> Result { + serde_json::to_string(&*self.proto_array.read()) + .map_err(|e| format!("Failed to JSON encode proto_array: {:?}", e)) + } } /// Returns a list of `deltas`, where there is one delta for each of the indices in