diff --git a/cli/src/commands/node/mod.rs b/cli/src/commands/node/mod.rs index fde5d76d9a..aa6b2897c1 100644 --- a/cli/src/commands/node/mod.rs +++ b/cli/src/commands/node/mod.rs @@ -226,10 +226,12 @@ impl Node { if let Some(producer_key_path) = self.producer_key { let password = &self.producer_key_password; - node::core::info!(node::core::log::system_time(); summary = "loading provers index"); - let provers = BlockProver::make(Some(block_verifier_index), Some(work_verifier_index)); - node::core::info!(node::core::log::system_time(); summary = "loaded provers index"); - node_builder.block_producer_from_file(provers, producer_key_path, password)?; + openmina_core::thread::spawn(|| { + node::core::info!(node::core::log::system_time(); summary = "loading provers index"); + BlockProver::make(Some(block_verifier_index), Some(work_verifier_index)); + node::core::info!(node::core::log::system_time(); summary = "loaded provers index"); + }); + node_builder.block_producer_from_file(producer_key_path, password, None)?; if let Some(pub_key) = self.coinbase_receiver { node_builder diff --git a/core/src/thread.rs b/core/src/thread.rs index daabd4fc67..7752288db6 100644 --- a/core/src/thread.rs +++ b/core/src/thread.rs @@ -73,6 +73,24 @@ mod main_thread { })); rx.await.ok() } + + pub fn run_async_fn_in_main_thread_blocking(f: F) -> Option + where + T: 'static + Send, + FU: Future, + F: 'static + Send + FnOnce() -> FU, + { + let sender = MAIN_THREAD_TASK_SENDER + .get() + .expect("main thread not initialized"); + let (tx, rx) = oneshot::channel(); + let _ = sender.send(Box::pin(async move { + wasm_bindgen_futures::spawn_local(async move { + let _ = tx.send(f().await); + }) + })); + rx.blocking_recv().ok() + } } #[cfg(target_family = "wasm")] pub use main_thread::*; diff --git a/frontend/docker/startup.sh b/frontend/docker/startup.sh index 7f167362f2..f92157a6d1 100644 --- a/frontend/docker/startup.sh +++ b/frontend/docker/startup.sh @@ -27,6 +27,8 @@ download_circuit_files() { CIRCUITS_VERSION="3.0.1devnet" DEVNET_CIRCUIT_FILES=( + "block_verifier_index.postcard" + "transaction_verifier_index.postcard" "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_gates.json" "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_internal_vars.bin" "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_rows_rev.bin" diff --git a/ledger/src/proofs/circuit_blobs.rs b/ledger/src/proofs/circuit_blobs.rs index ed89c42852..af19c7dc2a 100644 --- a/ledger/src/proofs/circuit_blobs.rs +++ b/ledger/src/proofs/circuit_blobs.rs @@ -36,6 +36,15 @@ mod http { _get_bytes(url).await } } + + pub fn get_bytes_blocking(url: &str) -> std::io::Result> { + let url = url.to_owned(); + if thread::is_web_worker_thread() { + thread::run_async_fn_in_main_thread_blocking(move || _get_bytes(url)).expect("failed to run task in the main thread! Maybe main thread crashed or not initialized?") + } else { + panic!("can't do blocking requests from main browser thread"); + } + } } #[cfg(not(target_family = "wasm"))] @@ -53,7 +62,7 @@ fn git_release_url(filename: &impl AsRef) -> String { } #[cfg(not(target_family = "wasm"))] -pub fn fetch(filename: &impl AsRef) -> std::io::Result> { +pub fn fetch_blocking(filename: &impl AsRef) -> std::io::Result> { use std::path::PathBuf; fn try_base_dir>(base_dir: P, filename: &impl AsRef) -> Option { @@ -124,3 +133,11 @@ pub async fn fetch(filename: &impl AsRef) -> std::io::Result> { http::get_bytes(&url).await // http::get_bytes(&git_release_url(filename)).await } + +#[cfg(target_family = "wasm")] +pub fn fetch_blocking(filename: &impl AsRef) -> std::io::Result> { + let prefix = + option_env!("CIRCUIT_BLOBS_HTTP_PREFIX").unwrap_or("/assets/webnode/circuit-blobs"); + let url = format!("{prefix}/{}", filename.as_ref().to_str().unwrap()); + http::get_bytes_blocking(&url) +} diff --git a/ledger/src/proofs/provers.rs b/ledger/src/proofs/provers.rs index 1e1da34c12..3afd8aa71c 100644 --- a/ledger/src/proofs/provers.rs +++ b/ledger/src/proofs/provers.rs @@ -50,23 +50,13 @@ fn decode_gates_file( Ok(data.gates) } -#[cfg(not(target_family = "wasm"))] fn read_gates_file( filename: &impl AsRef, ) -> std::io::Result>> { - let bytes = circuit_blobs::fetch(filename)?; + let bytes = circuit_blobs::fetch_blocking(filename)?; decode_gates_file(bytes.as_slice()) } -#[cfg(target_family = "wasm")] -async fn read_gates_file( - filepath: &impl AsRef, -) -> std::io::Result>> { - let resp = circuit_blobs::fetch(filepath).await?; - decode_gates_file(&mut resp.as_slice()) -} - -#[cfg(not(target_family = "wasm"))] fn make_gates( filename: &str, ) -> ( @@ -88,30 +78,6 @@ fn make_gates( (internal_vars_path, rows_rev_path, gates) } -#[cfg(target_family = "wasm")] -async fn make_gates( - filename: &str, -) -> ( - HashMap, Option)>, - Vec>>, - Vec>, -) { - let circuits_config = openmina_core::NetworkConfig::global().circuits_config; - let base_dir = Path::new(circuits_config.directory_name); - - let internal_vars_path = base_dir.join(format!("{}_internal_vars.bin", filename)); - let rows_rev_path = base_dir.join(format!("{}_rows_rev.bin", filename)); - let gates_path = base_dir.join(format!("{}_gates.json", filename)); - - let gates: Vec> = read_gates_file(&gates_path).await.unwrap(); - let (internal_vars_path, rows_rev_path) = - read_constraints_data::(&internal_vars_path, &rows_rev_path) - .await - .unwrap(); - - (internal_vars_path, rows_rev_path, gates) -} - macro_rules! get_or_make { ($constant: ident, $type: ty, $filename: expr) => {{ get_or_make!($constant, $type, None, $filename) @@ -121,13 +87,7 @@ macro_rules! get_or_make { return prover.clone(); } - let (internal_vars, rows_rev, gates) = { - #[cfg(not(target_family = "wasm"))] - let res = make_gates($filename); - #[cfg(target_family = "wasm")] - let res = make_gates($filename).await; - res - }; + let (internal_vars, rows_rev, gates) = make_gates($filename); let index = make_prover_index::<$type, _>(gates, $verifier_index); let prover = Prover { @@ -153,7 +113,6 @@ fn default_circuits_config() -> &'static CircuitsConfig { openmina_core::NetworkConfig::global().circuits_config } -#[cfg(not(target_family = "wasm"))] mod prover_makers { use super::*; @@ -274,128 +233,6 @@ mod prover_makers { } } -#[cfg(target_family = "wasm")] -mod prover_makers { - use super::*; - - async fn get_or_make_tx_step_prover(config: &CircuitsConfig) -> Arc> { - get_or_make!( - TX_STEP_PROVER, - StepTransactionProof, - config.step_transaction_gates - ) - } - async fn get_or_make_tx_wrap_prover( - config: &CircuitsConfig, - verifier_index: Option, - ) -> Arc> { - get_or_make!( - TX_WRAP_PROVER, - WrapTransactionProof, - verifier_index.map(Into::into), - config.wrap_transaction_gates - ) - } - async fn get_or_make_merge_step_prover(config: &CircuitsConfig) -> Arc> { - get_or_make!(MERGE_STEP_PROVER, StepMergeProof, config.step_merge_gates) - } - async fn get_or_make_block_step_prover(config: &CircuitsConfig) -> Arc> { - get_or_make!( - BLOCK_STEP_PROVER, - StepBlockProof, - config.step_blockchain_gates - ) - } - async fn get_or_make_block_wrap_prover( - config: &CircuitsConfig, - verifier_index: Option, - ) -> Arc> { - get_or_make!( - BLOCK_WRAP_PROVER, - WrapBlockProof, - verifier_index.map(Into::into), - config.wrap_blockchain_gates - ) - } - async fn get_or_make_zkapp_step_opt_signed_opt_signed_prover( - config: &CircuitsConfig, - ) -> Arc> { - get_or_make!( - ZKAPP_STEP_OPT_SIGNED_OPT_SIGNED_PROVER, - StepZkappOptSignedOptSignedProof, - config.step_transaction_opt_signed_opt_signed_gates - ) - } - async fn get_or_make_zkapp_step_opt_signed_prover(config: &CircuitsConfig) -> Arc> { - get_or_make!( - ZKAPP_STEP_OPT_SIGNED_PROVER, - StepZkappOptSignedProof, - config.step_transaction_opt_signed_gates - ) - } - async fn get_or_make_zkapp_step_proof_prover(config: &CircuitsConfig) -> Arc> { - get_or_make!( - ZKAPP_STEP_PROOF_PROVER, - StepZkappProvedProof, - config.step_transaction_proved_gates - ) - } - - impl BlockProver { - pub async fn make( - block_verifier_index: Option, - tx_verifier_index: Option, - ) -> Self { - let config = default_circuits_config(); - let block_step_prover = get_or_make_block_step_prover(config).await; - let block_wrap_prover = - get_or_make_block_wrap_prover(config, block_verifier_index).await; - let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await; - - Self { - block_step_prover, - block_wrap_prover, - tx_wrap_prover, - } - } - } - - impl TransactionProver { - pub async fn make(tx_verifier_index: Option) -> Self { - let config = default_circuits_config(); - let tx_step_prover = get_or_make_tx_step_prover(config).await; - let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await; - let merge_step_prover = get_or_make_merge_step_prover(config).await; - - Self { - tx_step_prover, - tx_wrap_prover, - merge_step_prover, - } - } - } - - impl ZkappProver { - pub async fn make(tx_verifier_index: Option) -> Self { - let config = default_circuits_config(); - let tx_wrap_prover = get_or_make_tx_wrap_prover(config, tx_verifier_index).await; - let merge_step_prover = get_or_make_merge_step_prover(config).await; - let step_opt_signed_opt_signed_prover = - get_or_make_zkapp_step_opt_signed_opt_signed_prover(config).await; - let step_opt_signed_prover = get_or_make_zkapp_step_opt_signed_prover(config).await; - let step_proof_prover = get_or_make_zkapp_step_proof_prover(config).await; - - Self { - tx_wrap_prover, - merge_step_prover, - step_opt_signed_opt_signed_prover, - step_opt_signed_prover, - step_proof_prover, - } - } - } -} - #[derive(Clone)] pub struct BlockProver { pub block_step_prover: Arc>, @@ -419,6 +256,18 @@ pub struct ZkappProver { pub step_proof_prover: Arc>, } +impl BlockProver { + /// Called must make sure that before calling this, + /// `BlockProver::make` call was made. + pub fn get_once_made() -> Self { + Self { + block_step_prover: BLOCK_STEP_PROVER.wait().clone(), + block_wrap_prover: BLOCK_WRAP_PROVER.wait().clone(), + tx_wrap_prover: TX_WRAP_PROVER.wait().clone(), + } + } +} + fn decode_constraints_data( mut internal_vars: &[u8], mut rows_rev: &[u8], @@ -471,31 +320,15 @@ fn decode_constraints_data( Some((internal_vars, rows_rev)) } -#[cfg(not(target_family = "wasm"))] fn read_constraints_data( internal_vars_path: &Path, rows_rev_path: &Path, ) -> Option<(InternalVars, Vec>>)> { // ((Fp.t * V.t) list * Fp.t option) - let internal_vars = circuit_blobs::fetch(&internal_vars_path).ok()?; + let internal_vars = circuit_blobs::fetch_blocking(&internal_vars_path).ok()?; // V.t option array list - let rows_rev = circuit_blobs::fetch(&rows_rev_path).ok()?; + let rows_rev = circuit_blobs::fetch_blocking(&rows_rev_path).ok()?; decode_constraints_data(internal_vars.as_slice(), rows_rev.as_slice()) } - -#[cfg(target_family = "wasm")] -async fn read_constraints_data( - internal_vars_path: &Path, - rows_rev_path: &Path, -) -> Option<(InternalVars, Vec>>)> { - // ((Fp.t * V.t) list * Fp.t option) - let internal_vars = circuit_blobs::fetch(&internal_vars_path).await.ok()?; - - // V.t option array list - let rows_rev = circuit_blobs::fetch(&rows_rev_path).await.ok()?; - // std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("rows_rev.bin")).unwrap(); - - decode_constraints_data(internal_vars.as_ref(), rows_rev.as_ref()) -} diff --git a/ledger/src/proofs/verifiers.rs b/ledger/src/proofs/verifiers.rs index 3ede6d9be7..a0acd68096 100644 --- a/ledger/src/proofs/verifiers.rs +++ b/ledger/src/proofs/verifiers.rs @@ -71,7 +71,7 @@ fn cache_path(kind: Kind) -> Option { macro_rules! read_cache { ($kind: expr, $digest: expr) => {{ #[cfg(not(target_family = "wasm"))] - let data = super::circuit_blobs::fetch(&cache_filename($kind)) + let data = super::circuit_blobs::fetch_blocking(&cache_filename($kind)) .context("fetching verifier index failed")?; #[cfg(target_family = "wasm")] let data = super::circuit_blobs::fetch(&cache_filename($kind)) diff --git a/node/common/src/service/block_producer/mod.rs b/node/common/src/service/block_producer/mod.rs index afb8288b79..df6427f2b2 100644 --- a/node/common/src/service/block_producer/mod.rs +++ b/node/common/src/service/block_producer/mod.rs @@ -16,16 +16,16 @@ use node::{ use crate::EventSender; pub struct BlockProducerService { - provers: BlockProver, + provers: Option, keypair: AccountSecretKey, vrf_evaluation_sender: mpsc::UnboundedSender, } impl BlockProducerService { pub fn new( - provers: BlockProver, keypair: AccountSecretKey, vrf_evaluation_sender: mpsc::UnboundedSender, + provers: Option, ) -> Self { Self { provers, @@ -35,9 +35,9 @@ impl BlockProducerService { } pub fn start( - provers: BlockProver, event_sender: EventSender, keypair: AccountSecretKey, + provers: Option, ) -> Self { let (vrf_evaluation_sender, vrf_evaluation_receiver) = mpsc::unbounded_channel::(); @@ -54,7 +54,7 @@ impl BlockProducerService { }) .unwrap(); - BlockProducerService::new(provers, keypair, vrf_evaluation_sender) + BlockProducerService::new(keypair, vrf_evaluation_sender, provers) } pub fn keypair(&self) -> AccountSecretKey { @@ -103,6 +103,7 @@ impl node::service::BlockProducerService for crate::NodeService { .expect("provers shouldn't be needed if block producer isn't initialized") .provers .clone() + .unwrap_or_else(BlockProver::get_once_made) } fn prove(&mut self, block_hash: StateHash, input: Box) { diff --git a/node/common/src/service/builder.rs b/node/common/src/service/builder.rs index 6ecbc9e3af..6b724ae3c3 100644 --- a/node/common/src/service/builder.rs +++ b/node/common/src/service/builder.rs @@ -80,13 +80,13 @@ impl NodeServiceCommonBuilder { pub fn block_producer_init( &mut self, - provers: BlockProver, keypair: AccountSecretKey, + provers: Option, ) -> &mut Self { self.block_producer = Some(BlockProducerService::start( - provers, self.event_sender.clone(), keypair, + provers, )); self } diff --git a/node/native/src/node/builder.rs b/node/native/src/node/builder.rs index f8400cd91b..dfe21a6406 100644 --- a/node/native/src/node/builder.rs +++ b/node/native/src/node/builder.rs @@ -174,27 +174,31 @@ impl NodeBuilder { } /// Set up block producer. - pub fn block_producer(&mut self, provers: BlockProver, key: AccountSecretKey) -> &mut Self { + pub fn block_producer( + &mut self, + key: AccountSecretKey, + provers: Option, + ) -> &mut Self { let config = BlockProducerConfig { pub_key: key.public_key().into(), custom_coinbase_receiver: None, proposed_protocol_version: None, }; self.block_producer = Some(config); - self.service.block_producer_init(provers, key); + self.service.block_producer_init(key, provers); self } /// Set up block producer using keys from file. pub fn block_producer_from_file( &mut self, - provers: BlockProver, path: impl AsRef, password: &str, + provers: Option, ) -> anyhow::Result<&mut Self> { let key = AccountSecretKey::from_encrypted_file(path, password) .context("Failed to decrypt secret key file")?; - Ok(self.block_producer(provers, key)) + Ok(self.block_producer(key, provers)) } /// Receive block producer's coinbase reward to another account. diff --git a/node/native/src/service/builder.rs b/node/native/src/service/builder.rs index a258f6f6ab..abb4d156ec 100644 --- a/node/native/src/service/builder.rs +++ b/node/native/src/service/builder.rs @@ -46,10 +46,10 @@ impl NodeServiceBuilder { pub fn block_producer_init( &mut self, - provers: BlockProver, keypair: AccountSecretKey, + provers: Option, ) -> &mut Self { - self.common.block_producer_init(provers, keypair); + self.common.block_producer_init(keypair, provers); self } diff --git a/node/src/block_producer/block_producer_actions.rs b/node/src/block_producer/block_producer_actions.rs index e234f33495..5b33c7f7cf 100644 --- a/node/src/block_producer/block_producer_actions.rs +++ b/node/src/block_producer/block_producer_actions.rs @@ -60,6 +60,7 @@ pub enum BlockProducerAction { proof: Box, }, BlockProduced, + #[action_event(level = trace)] BlockInject, BlockInjected, } diff --git a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs index 2b24054fba..d540a11d20 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs @@ -287,9 +287,7 @@ impl crate::State { let response = None.or_else(|| { let best_tip = best_chain.last()?; let mut chain_iter = best_chain.iter(); - let root_block = chain_iter.next(); - // when our best tip is genesis block. - let root_block = root_block.unwrap_or(best_tip); + let root_block = chain_iter.next()?; // TODO(binier): cache body hashes let Ok(body_hashes) = chain_iter .map(|b| b.header().protocol_state.body.try_hash()) diff --git a/node/src/state.rs b/node/src/state.rs index f62f3abdd3..a0b4735aed 100644 --- a/node/src/state.rs +++ b/node/src/state.rs @@ -290,12 +290,12 @@ impl State { } fn cur_slot(&self, initial_slot: impl FnOnce(&ArcBlockWithHash) -> u32) -> Option { - let best_tip = self.transition_frontier.best_tip()?; - let best_tip_ms = u64::from(best_tip.timestamp()) / 1_000_000; + let genesis = self.genesis_block()?; + let initial_ms = u64::from(genesis.timestamp()) / 1_000_000; let now_ms = u64::from(self.time()) / 1_000_000; - let ms = now_ms.saturating_sub(best_tip_ms); + let ms = now_ms.saturating_sub(initial_ms); let slots = ms / constraint_constants().block_window_duration_ms; - Some(initial_slot(best_tip) + slots as u32) + Some(initial_slot(&genesis) + slots as u32) } /// Current global slot based on constants and current time. @@ -306,7 +306,8 @@ impl State { } pub fn current_slot(&self) -> Option { - self.cur_slot(|b| b.global_slot() % b.constants().slots_per_epoch.as_u32()) + let slots_per_epoch = self.genesis_block()?.constants().slots_per_epoch.as_u32(); + Some(self.cur_global_slot()? % slots_per_epoch) } pub fn cur_global_slot_since_genesis(&self) -> Option { diff --git a/node/src/transition_frontier/genesis/transition_frontier_genesis_config.rs b/node/src/transition_frontier/genesis/transition_frontier_genesis_config.rs index 9275e50e67..238b6425fb 100644 --- a/node/src/transition_frontier/genesis/transition_frontier_genesis_config.rs +++ b/node/src/transition_frontier/genesis/transition_frontier_genesis_config.rs @@ -429,7 +429,7 @@ impl GenesisConfig { account } - let mut accounts = Vec::new(); + let mut accounts = genesis_account_iter().map(Ok).collect::>(); // Process block producers and their delegators for (bp_balance, delegators) in block_producers { @@ -476,11 +476,6 @@ impl GenesisConfig { } } - // Add genesis accounts - for genesis_account in genesis_account_iter() { - accounts.push(Ok(genesis_account)); - } - Self::build_ledger_from_accounts(accounts) } diff --git a/node/testing/src/cluster/mod.rs b/node/testing/src/cluster/mod.rs index 8a39c95553..69f976b924 100644 --- a/node/testing/src/cluster/mod.rs +++ b/node/testing/src/cluster/mod.rs @@ -321,7 +321,7 @@ impl Cluster { if let Some(keypair) = block_producer_sec_key { let provers = BlockProver::make(None, None); - service_builder.block_producer_init(provers, keypair); + service_builder.block_producer_init(keypair, Some(provers)); } let real_service = service_builder diff --git a/node/web/src/lib.rs b/node/web/src/lib.rs index 5d8c0a5f89..d7c4c10946 100644 --- a/node/web/src/lib.rs +++ b/node/web/src/lib.rs @@ -45,7 +45,7 @@ pub async fn run(block_producer: Option) -> RpcSender { node.run_forever().await; }); - wasm_bindgen::throw_str("Cursed hack to keep workers alive. See https://github.com/rustwasm/wasm-bindgen/issues/2945"); + keep_worker_alive_cursed_hack(); }); rpc_sender_rx.await.unwrap() @@ -64,9 +64,10 @@ async fn setup_node( .work_verifier_index(work_verifier_index.clone()); if let Some(bp_key) = block_producer { - let provers = - BlockProver::make(Some(block_verifier_index), Some(work_verifier_index)).await; - node_builder.block_producer(provers, bp_key); + thread::spawn(move || { + BlockProver::make(Some(block_verifier_index), Some(work_verifier_index)); + }); + node_builder.block_producer(bp_key, None); } node_builder @@ -75,3 +76,7 @@ async fn setup_node( node_builder.gather_stats(); node_builder.build().context("node build failed!").unwrap() } + +fn keep_worker_alive_cursed_hack() { + wasm_bindgen::throw_str("Cursed hack to keep workers alive. See https://github.com/rustwasm/wasm-bindgen/issues/2945"); +} diff --git a/node/web/src/node/builder.rs b/node/web/src/node/builder.rs index 70c9a119c8..b9e93beac6 100644 --- a/node/web/src/node/builder.rs +++ b/node/web/src/node/builder.rs @@ -117,14 +117,18 @@ impl NodeBuilder { } /// Set up block producer. - pub fn block_producer(&mut self, provers: BlockProver, key: AccountSecretKey) -> &mut Self { + pub fn block_producer( + &mut self, + key: AccountSecretKey, + provers: Option, + ) -> &mut Self { let config = BlockProducerConfig { pub_key: key.public_key().into(), custom_coinbase_receiver: None, proposed_protocol_version: None, }; self.block_producer = Some(config); - self.service.block_producer_init(provers, key); + self.service.block_producer_init(key, provers); self } diff --git a/p2p/src/disconnection/p2p_disconnection_actions.rs b/p2p/src/disconnection/p2p_disconnection_actions.rs index aec90cef41..6a9b011e8d 100644 --- a/p2p/src/disconnection/p2p_disconnection_actions.rs +++ b/p2p/src/disconnection/p2p_disconnection_actions.rs @@ -7,7 +7,7 @@ use crate::{P2pPeerStatus, P2pState, PeerId}; pub type P2pDisconnectionActionWithMetaRef<'a> = redux::ActionWithMeta<&'a P2pDisconnectionAction>; #[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] -#[action_event(fields(display(peer_id), display(reason)), level = info)] +#[action_event(level = debug)] pub enum P2pDisconnectionAction { /// Initialize disconnection. #[action_event(fields(display(peer_id), display(reason)), level = info)] @@ -16,7 +16,7 @@ pub enum P2pDisconnectionAction { reason: P2pDisconnectionReason, }, /// Finish disconnecting from a peer. - #[action_event(level = debug)] + #[action_event(fields(display(peer_id)), level = debug)] Finish { peer_id: PeerId }, }