diff --git a/block-producer/src/config.rs b/block-producer/src/config.rs index b544a3aa5..83542aaad 100644 --- a/block-producer/src/config.rs +++ b/block-producer/src/config.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter}; + use miden_node_utils::config::Endpoint; use serde::{Deserialize, Serialize}; @@ -17,7 +19,19 @@ pub struct BlockProducerConfig { impl BlockProducerConfig { pub fn as_url(&self) -> String { - format!("http://{}:{}", self.endpoint.host, self.endpoint.port) + self.endpoint.to_string() + } +} + +impl Display for BlockProducerConfig { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!( + "{{ endpoint: \"{}\", store_url: \"{}\" }}", + self.endpoint, self.store_url + )) } } diff --git a/block-producer/src/server/api.rs b/block-producer/src/server/api.rs index 5c3ee1a01..a8d456f0d 100644 --- a/block-producer/src/server/api.rs +++ b/block-producer/src/server/api.rs @@ -6,10 +6,12 @@ use miden_node_proto::{ block_producer::api_server, requests::SubmitProvenTransactionRequest, responses::SubmitProvenTransactionResponse, }; +use miden_node_utils::logging::{format_input_notes, format_opt, format_output_notes}; use miden_objects::transaction::ProvenTransaction; use tonic::Status; +use tracing::{debug, info, instrument}; -use crate::txqueue::TransactionQueue; +use crate::{txqueue::TransactionQueue, COMPONENT}; // BLOCK PRODUCER // ================================================================================================ @@ -32,15 +34,37 @@ impl api_server::Api for BlockProducerApi where T: TransactionQueue, { + #[allow(clippy::blocks_in_conditions)] // Workaround of `instrument` issue + #[instrument( + target = "miden-block-producer", + name = "block_producer::submit_proven_transaction", + skip_all, + err + )] async fn submit_proven_transaction( &self, request: tonic::Request, ) -> Result, Status> { let request = request.into_inner(); + debug!(target: COMPONENT, tx = ?request.transaction); let tx = ProvenTransaction::read_from_bytes(&request.transaction) .map_err(|_| Status::invalid_argument("Invalid transaction"))?; + info!( + target: COMPONENT, + tx_id = %tx.id().to_hex(), + account_id = %tx.account_id().to_hex(), + initial_account_hash = %tx.initial_account_hash(), + final_account_hash = %tx.final_account_hash(), + input_notes = %format_input_notes(tx.input_notes()), + output_notes = %format_output_notes(tx.output_notes()), + tx_script_root = %format_opt(tx.tx_script_root().as_ref()), + block_ref = %tx.block_ref(), + "Deserialized transaction" + ); + debug!(target: COMPONENT, proof = ?tx.proof()); + self.queue .add_transaction(Arc::new(tx)) .await diff --git a/block-producer/src/server/mod.rs b/block-producer/src/server/mod.rs index 539e0ad7d..d5abae51e 100644 --- a/block-producer/src/server/mod.rs +++ b/block-producer/src/server/mod.rs @@ -3,7 +3,7 @@ use std::{net::ToSocketAddrs, sync::Arc}; use anyhow::{anyhow, Result}; use miden_node_proto::{block_producer::api_server, store::api_client as store_client}; use tonic::transport::Server; -use tracing::info; +use tracing::{info, info_span, instrument, Instrument}; use crate::{ batch_builder::{DefaultBatchBuilder, DefaultBatchBuilderOptions}, @@ -23,7 +23,10 @@ pub mod api; // ================================================================================================ /// TODO: add comments +#[instrument(target = "miden-block-producer", skip_all)] pub async fn serve(config: BlockProducerConfig) -> Result<()> { + info!(target: COMPONENT, %config, "Initializing server"); + let store = Arc::new(DefaultStore::new( store_client::ApiClient::connect(config.store_url.to_string()).await?, )); @@ -50,21 +53,21 @@ pub async fn serve(config: BlockProducerConfig) -> Result<()> { let block_producer = api_server::ApiServer::new(api::BlockProducerApi::new(queue.clone())); tokio::spawn(async move { - info!(COMPONENT, "transaction queue started"); - queue.run().await + queue + .run() + .instrument(info_span!(target: COMPONENT, "transaction_queue_start")) + .await }); tokio::spawn(async move { - info!(COMPONENT, "batch builder started"); - batch_builder.run().await + batch_builder + .run() + .instrument(info_span!(target: COMPONENT, "batch_builder_start")) + .await }); - info!( - COMPONENT, - host = config.endpoint.host, - port = config.endpoint.port, - "Server initialized", - ); + info!(target: COMPONENT, "Server initialized"); + let addr = config .endpoint .to_socket_addrs()? diff --git a/block-producer/src/state_view/mod.rs b/block-producer/src/state_view/mod.rs index 58a5dcbd4..7fdcf9924 100644 --- a/block-producer/src/state_view/mod.rs +++ b/block-producer/src/state_view/mod.rs @@ -1,14 +1,16 @@ use std::{collections::BTreeSet, sync::Arc}; use async_trait::async_trait; +use miden_node_utils::logging::format_array; use miden_objects::{accounts::AccountId, notes::Nullifier, transaction::InputNotes, Digest}; use tokio::sync::RwLock; +use tracing::{debug, instrument}; use crate::{ block::Block, store::{ApplyBlock, ApplyBlockError, Store, TxInputs}, txqueue::{TransactionVerifier, VerifyTxError}, - SharedProvenTx, + SharedProvenTx, COMPONENT, }; #[cfg(test)] @@ -44,6 +46,8 @@ where S: Store, { // TODO: Verify proof as well + #[allow(clippy::blocks_in_conditions)] // Workaround of `instrument` issue + #[instrument(skip_all, err)] async fn verify_tx( &self, candidate_tx: SharedProvenTx, @@ -132,11 +136,14 @@ where /// transaction (issue: #186) /// 2. no consumed note's nullifier in candidate tx's consumed notes is already contained in /// `already_consumed_nullifiers` +#[instrument(target = "miden-block-producer", skip_all, err)] fn ensure_in_flight_constraints( candidate_tx: SharedProvenTx, accounts_in_flight: &BTreeSet, already_consumed_nullifiers: &BTreeSet, ) -> Result<(), VerifyTxError> { + debug!(target: COMPONENT, accounts_in_flight = %format_array(accounts_in_flight), already_consumed_nullifiers = %format_array(already_consumed_nullifiers)); + // 1. Check account id hasn't been modified yet if accounts_in_flight.contains(&candidate_tx.account_id()) { return Err(VerifyTxError::AccountAlreadyModifiedByOtherTx(candidate_tx.account_id())); @@ -163,10 +170,13 @@ fn ensure_in_flight_constraints( Ok(()) } +#[instrument(target = "miden-block-producer", skip_all, err)] fn ensure_tx_inputs_constraints( candidate_tx: SharedProvenTx, tx_inputs: TxInputs, ) -> Result<(), VerifyTxError> { + debug!(target: COMPONENT, %tx_inputs); + match tx_inputs.account_hash { Some(store_account_hash) => { if candidate_tx.initial_account_hash() != store_account_hash { diff --git a/block-producer/src/store/mod.rs b/block-producer/src/store/mod.rs index 368de5a73..11e5fa0d8 100644 --- a/block-producer/src/store/mod.rs +++ b/block-producer/src/store/mod.rs @@ -1,4 +1,7 @@ -use std::collections::BTreeMap; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, +}; use async_trait::async_trait; use miden_node_proto::{ @@ -11,11 +14,13 @@ use miden_node_proto::{ }; use miden_objects::{accounts::AccountId, Digest}; use tonic::transport::Channel; +use tracing::{debug, info, instrument}; -use crate::{block::Block, SharedProvenTx}; +use crate::{block::Block, SharedProvenTx, COMPONENT}; mod errors; pub use errors::{ApplyBlockError, BlockInputsError, TxInputsError}; +use miden_node_utils::logging::{format_map, format_opt}; // STORE TRAIT // ================================================================================================ @@ -45,6 +50,7 @@ pub trait ApplyBlock: Send + Sync + 'static { } /// Information needed from the store to verify a transaction. +#[derive(Debug)] pub struct TxInputs { /// The account hash in the store corresponding to tx's account ID pub account_hash: Option, @@ -53,6 +59,19 @@ pub struct TxInputs { pub nullifiers: BTreeMap, } +impl Display for TxInputs { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!( + "{{ account_hash: {}, nullifiers: {} }}", + format_opt(self.account_hash.as_ref()), + format_map(&self.nullifiers) + )) + } +} + // DEFAULT STORE IMPLEMENTATION // ================================================================================================ @@ -93,18 +112,25 @@ impl ApplyBlock for DefaultStore { #[async_trait] impl Store for DefaultStore { + #[allow(clippy::blocks_in_conditions)] // Workaround of `instrument` issue + #[instrument(target = "miden-block-producer", skip_all, err)] async fn get_tx_inputs( &self, proven_tx: SharedProvenTx, ) -> Result { - let request = tonic::Request::new(GetTransactionInputsRequest { + let message = GetTransactionInputsRequest { account_id: Some(proven_tx.account_id().into()), nullifiers: proven_tx .input_notes() .iter() .map(|nullifier| (*nullifier).into()) .collect(), - }); + }; + + info!(target: COMPONENT, tx_id = %proven_tx.id().to_hex()); + debug!(target: COMPONENT, ?message); + + let request = tonic::Request::new(message); let response = self .store .clone() @@ -113,6 +139,8 @@ impl Store for DefaultStore { .map_err(|status| TxInputsError::GrpcClientError(status.message().to_string()))? .into_inner(); + debug!(target: COMPONENT, ?response); + let account_hash = { let account_state = response .account_state @@ -153,10 +181,14 @@ impl Store for DefaultStore { nullifiers.into_iter().collect() }; - Ok(TxInputs { + let tx_inputs = TxInputs { account_hash, nullifiers, - }) + }; + + debug!(target: COMPONENT, %tx_inputs); + + Ok(tx_inputs) } async fn get_block_inputs( diff --git a/block-producer/src/txqueue/mod.rs b/block-producer/src/txqueue/mod.rs index 02e7bd085..559844be8 100644 --- a/block-producer/src/txqueue/mod.rs +++ b/block-producer/src/txqueue/mod.rs @@ -1,12 +1,19 @@ -use std::{fmt::Debug, sync::Arc, time::Duration}; +use std::{ + fmt::{Debug, Display, Formatter}, + sync::Arc, + time::Duration, +}; use async_trait::async_trait; use miden_objects::{ accounts::AccountId, notes::Nullifier, transaction::InputNotes, Digest, TransactionInputError, }; use tokio::{sync::RwLock, time}; +use tracing::{info, instrument}; -use crate::{batch_builder::BatchBuilder, store::TxInputsError, SharedProvenTx, SharedRwVec}; +use crate::{ + batch_builder::BatchBuilder, store::TxInputsError, SharedProvenTx, SharedRwVec, COMPONENT, +}; #[cfg(test)] mod tests; @@ -38,6 +45,15 @@ pub enum VerifyTxError { TransactionInputError(TransactionInputError), } +impl Display for VerifyTxError { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + Debug::fmt(self, f) + } +} + impl From for VerifyTxError { fn from(err: TxInputsError) -> Self { Self::StoreConnectionFailed(err) @@ -76,6 +92,15 @@ pub enum AddTransactionError { VerificationFailed(VerifyTxError), } +impl Display for AddTransactionError { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + Debug::fmt(self, f) + } +} + // TRANSACTION QUEUE // ================================================================================================ @@ -171,16 +196,26 @@ where TV: TransactionVerifier, BB: BatchBuilder, { + #[allow(clippy::blocks_in_conditions)] // Workaround of `instrument` issue + #[instrument(target = "miden-block-producer", skip_all, err)] async fn add_transaction( &self, tx: SharedProvenTx, ) -> Result<(), AddTransactionError> { + info!(target: COMPONENT, tx_id = %tx.id().to_hex(), account_id = %tx.account_id().to_hex()); + self.tx_verifier .verify_tx(tx.clone()) .await .map_err(AddTransactionError::VerificationFailed)?; - self.ready_queue.write().await.push(tx); + let queue_len = { + let mut queue_write_guard = self.ready_queue.write().await; + queue_write_guard.push(tx); + queue_write_guard.len() + }; + + info!(target: COMPONENT, queue_len, "Transaction added to tx queue"); Ok(()) } diff --git a/proto/Cargo.toml b/proto/Cargo.toml index bd7de4148..2721e0b1b 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -22,5 +22,6 @@ proptest = { version = "1.2" } [build-dependencies] miette = { version = "5.9", features = ["fancy"] } prost = { version = "0.12" } +prost-build = { version = "0.12" } protox = { version = "0.5" } tonic-build = { version = "0.10" } diff --git a/proto/build.rs b/proto/build.rs index fc8bb8e82..9f0d51a01 100644 --- a/proto/build.rs +++ b/proto/build.rs @@ -22,13 +22,16 @@ fn main() -> miette::Result<()> { let file_descriptors = protox::compile(protos, includes)?; fs::write(&file_descriptor_path, file_descriptors.encode_to_vec()).into_diagnostic()?; + let mut prost_config = prost_build::Config::new(); + prost_config.skip_debug(["AccountId", "Digest"]); + // Generate the stub of the user facing server from its proto file tonic_build::configure() .file_descriptor_set_path(&file_descriptor_path) .type_attribute(".", "#[derive(Eq, PartialOrd, Ord, Hash)]") .skip_protoc_run() .out_dir("src/generated") - .compile(protos, includes) + .compile_with_config(prost_config, protos, includes) .into_diagnostic()?; Ok(()) diff --git a/proto/src/formatting.rs b/proto/src/formatting.rs new file mode 100644 index 000000000..afa92dcb0 --- /dev/null +++ b/proto/src/formatting.rs @@ -0,0 +1,41 @@ +use std::fmt::{Debug, Display, Formatter}; + +use hex::ToHex; + +use crate::{account::AccountId, digest::Digest}; + +impl Display for AccountId { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!("0x{:x}", self.id)) + } +} + +impl Debug for AccountId { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl Display for Digest { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_str(&self.encode_hex::()) + } +} + +impl Debug for Digest { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + Display::fmt(self, f) + } +} diff --git a/proto/src/generated/account.rs b/proto/src/generated/account.rs index ca12ee6df..7f2055c72 100644 --- a/proto/src/generated/account.rs +++ b/proto/src/generated/account.rs @@ -1,6 +1,7 @@ #[derive(Eq, PartialOrd, Ord, Hash)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +#[prost(skip_debug)] pub struct AccountId { /// A miden account is defined with a little bit of proof-of-work, the id itself is defined as /// the first word of a hash digest. For this reason account ids can be considered as random diff --git a/proto/src/generated/digest.rs b/proto/src/generated/digest.rs index a4278ff9a..f120dc152 100644 --- a/proto/src/generated/digest.rs +++ b/proto/src/generated/digest.rs @@ -2,6 +2,7 @@ #[derive(Eq, PartialOrd, Ord, Hash)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +#[prost(skip_debug)] pub struct Digest { #[prost(fixed64, tag = "1")] pub d0: u64, diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 1b2d7d795..b70baa919 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -1,12 +1,12 @@ pub mod conversion; pub mod domain; pub mod error; +mod formatting; +pub mod hex; #[rustfmt::skip] mod generated; -pub mod hex; - // RE-EXPORTS // ------------------------------------------------------------------------------------------------ pub use generated::{ diff --git a/rpc/src/config.rs b/rpc/src/config.rs index c96e8b97b..ab6b8e0a4 100644 --- a/rpc/src/config.rs +++ b/rpc/src/config.rs @@ -1,3 +1,5 @@ +use std::fmt::{Display, Formatter}; + use miden_node_utils::config::Endpoint; use serde::{Deserialize, Serialize}; @@ -17,7 +19,19 @@ pub struct RpcConfig { impl RpcConfig { pub fn as_url(&self) -> String { - format!("http://{}:{}", self.endpoint.host, self.endpoint.port) + self.endpoint.to_string() + } +} + +impl Display for RpcConfig { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!( + "{{ endpoint: \"{}\", store_url: \"{}\", block_producer_url: \"{}\" }}", + self.endpoint, self.store_url, self.block_producer_url + )) } } diff --git a/rpc/src/server/api.rs b/rpc/src/server/api.rs index b24a2ade7..9a2395e76 100644 --- a/rpc/src/server/api.rs +++ b/rpc/src/server/api.rs @@ -17,7 +17,7 @@ use tonic::{ transport::{Channel, Error}, Request, Response, Status, }; -use tracing::info; +use tracing::{info, instrument}; use crate::{config::RpcConfig, COMPONENT}; @@ -32,12 +32,12 @@ pub struct RpcApi { impl RpcApi { pub(super) async fn from_config(config: &RpcConfig) -> Result { let store = store_client::ApiClient::connect(config.store_url.clone()).await?; - info!(COMPONENT, store_endpoint = config.store_url, "Store client initialized"); + info!(target: COMPONENT, store_endpoint = config.store_url, "Store client initialized"); let block_producer = block_producer_client::ApiClient::connect(config.block_producer_url.clone()).await?; info!( - COMPONENT, + target: COMPONENT, block_producer_endpoint = config.block_producer_url, "Block producer client initialized", ); @@ -79,6 +79,7 @@ impl api_server::Api for RpcApi { self.store.clone().sync_state(request).await } + #[instrument(target = "miden-rpc", name = "rpc::submit_proven_transaction", skip_all)] async fn submit_proven_transaction( &self, request: Request, diff --git a/rpc/src/server/mod.rs b/rpc/src/server/mod.rs index ed2b520a1..585c875b0 100644 --- a/rpc/src/server/mod.rs +++ b/rpc/src/server/mod.rs @@ -3,7 +3,7 @@ use std::net::ToSocketAddrs; use anyhow::{anyhow, Result}; use miden_node_proto::rpc::api_server; use tonic::transport::Server; -use tracing::info; +use tracing::{info, instrument}; use crate::{config::RpcConfig, COMPONENT}; @@ -11,17 +11,14 @@ mod api; // RPC INITIALIZER // ================================================================================================ - +#[instrument(target = "miden-rpc", skip_all)] pub async fn serve(config: RpcConfig) -> Result<()> { + info!(target: COMPONENT, %config, "Initializing server"); + let api = api::RpcApi::from_config(&config).await?; let rpc = api_server::ApiServer::new(api); - info!( - host = config.endpoint.host, - port = config.endpoint.port, - COMPONENT, - "Server initialized" - ); + info!(target: COMPONENT, "Server initialized"); let addr = config .endpoint diff --git a/store/src/config.rs b/store/src/config.rs index 6cae6c15a..120d7cdf0 100644 --- a/store/src/config.rs +++ b/store/src/config.rs @@ -1,4 +1,7 @@ -use std::path::PathBuf; +use std::{ + fmt::{Display, Formatter}, + path::PathBuf, +}; use miden_node_utils::config::Endpoint; use serde::{Deserialize, Serialize}; @@ -20,7 +23,19 @@ pub struct StoreConfig { impl StoreConfig { pub fn as_url(&self) -> String { - format!("http://{}:{}", self.endpoint.host, self.endpoint.port) + self.endpoint.to_string() + } +} + +impl Display for StoreConfig { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!( + "{{ endpoint: \"{}\", database_filepath: {:?}, genesis_filepath: {:?} }}", + self.endpoint, self.database_filepath, self.genesis_filepath + )) } } diff --git a/store/src/db/mod.rs b/store/src/db/mod.rs index 96153ca21..fc2df50ed 100644 --- a/store/src/db/mod.rs +++ b/store/src/db/mod.rs @@ -12,7 +12,7 @@ use miden_node_proto::{ }; use rusqlite::vtab::array; use tokio::sync::oneshot; -use tracing::{info, span, Level}; +use tracing::{info, instrument, span, Level}; use self::errors::GenesisBlockError; use crate::{ @@ -45,7 +45,10 @@ pub struct StateSyncUpdate { impl Db { /// Open a connection to the DB, apply any pending migrations, and ensure that the genesis block /// is as expected and present in the database. + #[instrument(target = "miden-store", skip_all)] pub async fn setup(config: StoreConfig) -> Result { + info!(target: COMPONENT, %config, "Connecting to the database"); + if let Some(p) = config.database_filepath.parent() { create_dir_all(p)?; } @@ -79,8 +82,9 @@ impl Db { .build()?; info!( + target: COMPONENT, sqlite = format!("{}", config.database_filepath.display()), - COMPONENT, "Connected to the DB" + "Connected to the database" ); let conn = pool.get().await?; diff --git a/store/src/main.rs b/store/src/main.rs index fc41220f9..81e53df22 100644 --- a/store/src/main.rs +++ b/store/src/main.rs @@ -43,8 +43,7 @@ async fn query( config: StoreTopLevelConfig, command: Query, ) -> Result<()> { - let endpoint = format!("http://{}:{}", config.store.endpoint.host, config.store.endpoint.port); - let mut client = api_client::ApiClient::connect(endpoint).await?; + let mut client = api_client::ApiClient::connect(config.store.endpoint.to_string()).await?; match command { Query::GetBlockHeaderByNumber(args) => { diff --git a/store/src/server/api.rs b/store/src/server/api.rs index 883760bb7..4d2f66744 100644 --- a/store/src/server/api.rs +++ b/store/src/server/api.rs @@ -20,8 +20,9 @@ use miden_node_proto::{ tsmt::NullifierLeaf, }; use tonic::{Response, Status}; +use tracing::{debug, instrument}; -use crate::state::State; +use crate::{state::State, COMPONENT}; // STORE API // ================================================================================================ @@ -156,12 +157,22 @@ impl api_server::Api for StoreApi { })) } + #[allow(clippy::blocks_in_conditions)] // Workaround of `instrument` issue + #[instrument( + target = "miden-store", + name = "store::get_transaction_inputs", + skip_all, + ret(level = "debug"), + err + )] async fn get_transaction_inputs( &self, request: tonic::Request, ) -> Result, Status> { let request = request.into_inner(); + debug!(target: COMPONENT, ?request); + let nullifiers = validate_nullifiers(&request.nullifiers)?; let account_id = request.account_id.ok_or(invalid_argument("Account_id missing"))?.id; diff --git a/store/src/server/mod.rs b/store/src/server/mod.rs index 4915be362..e74c777cf 100644 --- a/store/src/server/mod.rs +++ b/store/src/server/mod.rs @@ -12,20 +12,17 @@ mod api; // STORE INITIALIZER // ================================================================================================ -#[instrument(skip(config, db))] +#[instrument(target = "miden-store", skip_all)] pub async fn serve( config: StoreConfig, db: Db, ) -> Result<()> { + info!(target: COMPONENT, %config, "Initializing server"); + let state = Arc::new(State::load(db).await?); let store = api_server::ApiServer::new(api::StoreApi { state }); - info!( - host = config.endpoint.host, - port = config.endpoint.port, - COMPONENT, - "Server initialized", - ); + info!(target: COMPONENT, "Server initialized"); let addr = config .endpoint diff --git a/store/src/state.rs b/store/src/state.rs index 09f77a23b..e268d44ae 100644 --- a/store/src/state.rs +++ b/store/src/state.rs @@ -2,7 +2,11 @@ //! //! The [State] provides data access and modifications methods, its main purpose is to ensure that //! data is atomically written, and that reads are consistent. -use std::{mem, sync::Arc}; +use std::{ + fmt::{Debug, Display, Formatter}, + mem, + sync::Arc, +}; use anyhow::{anyhow, bail, Result}; use miden_crypto::{ @@ -25,6 +29,7 @@ use miden_node_proto::{ AccountBlockInputRecord, AccountTransactionInputRecord, NullifierTransactionInputRecord, }, }; +use miden_node_utils::logging::{format_account_id, format_array}; use miden_objects::{ notes::{NoteMetadata, NOTE_LEAF_DEPTH}, BlockHeader, ACCOUNT_TREE_DEPTH, @@ -87,6 +92,28 @@ pub struct AccountState { account_hash: Word, } +impl Debug for AccountState { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!( + "{{ account_id: {}, account_hash: {} }}", + format_account_id(self.account_id), + RpoDigest::from(self.account_hash), + )) + } +} + +impl Display for AccountState { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + Debug::fmt(self, f) + } +} + impl From for AccountTransactionInputRecord { fn from(value: AccountState) -> Self { Self { @@ -119,11 +146,24 @@ impl TryFrom<&AccountUpdate> for AccountState { } } +#[derive(Debug)] pub struct NullifierStateForTransactionInput { nullifier: RpoDigest, block_num: u32, } +impl Display for NullifierStateForTransactionInput { + fn fmt( + &self, + formatter: &mut Formatter<'_>, + ) -> std::fmt::Result { + formatter.write_fmt(format_args!( + "{{ nullifier: {}, block_num: {} }}", + self.nullifier, self.block_num + )) + } +} + impl From for NullifierTransactionInputRecord { fn from(value: NullifierStateForTransactionInput) -> Self { Self { @@ -135,7 +175,7 @@ impl From for NullifierTransactionInputRecord impl State { /// Loads the state from the `db`. - #[instrument(skip(db))] + #[instrument(target = "miden-store", skip_all)] pub async fn load(mut db: Db) -> Result { let nullifier_tree = load_nullifier_tree(&mut db).await?; let chain_mmr = load_mmr(&mut db).await?; @@ -444,11 +484,14 @@ impl State { } /// Returns data needed by the block producer to verify transactions validity. + #[instrument(target = "miden-store", skip_all, ret, err)] pub async fn get_transaction_inputs( &self, account_id: AccountId, nullifiers: &[RpoDigest], ) -> Result<(AccountState, Vec), anyhow::Error> { + info!(target: COMPONENT, account_id = %format_account_id(account_id), nullifiers = %format_array(nullifiers)); + let inner = self.inner.read().await; let account = AccountState { diff --git a/utils/Cargo.toml b/utils/Cargo.toml index b2b170dd1..fba40556a 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [dependencies] anyhow = { version = "1.0" } figment = { version = "0.10", features = ["toml", "env"] } +itertools = { version = "0.12" } +miden-crypto = { workspace = true } +miden_objects = { workspace = true } serde = { version = "1.0", features = ["derive"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/utils/src/config.rs b/utils/src/config.rs index f7f517a97..4cb2d81f9 100644 --- a/utils/src/config.rs +++ b/utils/src/config.rs @@ -1,4 +1,5 @@ use std::{ + fmt::{Display, Formatter}, io, net::{SocketAddr, ToSocketAddrs}, path::Path, @@ -27,6 +28,15 @@ impl ToSocketAddrs for Endpoint { } } +impl Display for Endpoint { + fn fmt( + &self, + f: &mut Formatter<'_>, + ) -> std::fmt::Result { + f.write_fmt(format_args!("http://{}:{}", self.host, self.port)) + } +} + /// Loads the user configuration. /// /// This function will look for the configuration file at the provided path. If the path is diff --git a/utils/src/logging.rs b/utils/src/logging.rs index 8bc937102..4bc0ef0c3 100644 --- a/utils/src/logging.rs +++ b/utils/src/logging.rs @@ -1,4 +1,11 @@ +use std::fmt::Display; + use anyhow::Result; +use itertools::Itertools; +use miden_objects::{ + notes::{NoteEnvelope, Nullifier}, + transaction::{InputNotes, OutputNotes}, +}; use tracing::{level_filters::LevelFilter, subscriber}; use tracing_subscriber::{self, fmt::format::FmtSpan, EnvFilter}; @@ -9,7 +16,7 @@ pub fn setup_logging() -> Result<()> { .with_level(true) .with_file(true) .with_line_number(true) - .with_target(false) + .with_target(true) .with_env_filter( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) @@ -21,3 +28,47 @@ pub fn setup_logging() -> Result<()> { Ok(()) } + +pub fn format_account_id(id: u64) -> String { + format!("0x{id:x}") +} + +pub fn format_opt(opt: Option<&T>) -> String { + opt.map(ToString::to_string).unwrap_or("None".to_owned()) +} + +pub fn format_input_notes(notes: &InputNotes) -> String { + format_array(notes.iter().map(Nullifier::to_hex)) +} + +pub fn format_output_notes(notes: &OutputNotes) -> String { + format_array(notes.iter().map(|envelope| { + let metadata = envelope.metadata(); + format!( + "{{ note_id: {}, note_metadata: {{sender: {}, tag: {} }}}}", + envelope.note_id().to_hex(), + metadata.sender(), + metadata.tag(), + ) + })) +} + +pub fn format_map<'a, K: Display + 'a, V: Display + 'a>( + map: impl IntoIterator +) -> String { + let map_str = map.into_iter().map(|(key, val)| format!("{key}: {val}")).join(", "); + if map_str.is_empty() { + "None".to_owned() + } else { + format!("{{ {} }}", map_str) + } +} + +pub fn format_array(list: impl IntoIterator) -> String { + let comma_separated = list.into_iter().join(", "); + if comma_separated.is_empty() { + "None".to_owned() + } else { + format!("[{}]", comma_separated) + } +}