From fcb66c796cd6a868f778697b411a5c1a9e7b2348 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:16:11 +0500 Subject: [PATCH 01/29] feat: improve node errors --- crates/store/src/errors.rs | 16 +++++- crates/store/src/server/api.rs | 89 +++++++++++++--------------------- crates/store/src/state.rs | 4 +- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/crates/store/src/errors.rs b/crates/store/src/errors.rs index 2666c138..700c9d3c 100644 --- a/crates/store/src/errors.rs +++ b/crates/store/src/errors.rs @@ -14,6 +14,7 @@ use miden_objects::{ use rusqlite::types::FromSqlError; use thiserror::Error; use tokio::sync::oneshot::error::RecvError; +use tonic::Status; use crate::types::{AccountId, BlockNumber}; @@ -60,8 +61,6 @@ pub enum DatabaseError { InteractError(String), #[error("Deserialization of BLOB data from database failed: {0}")] DeserializationError(DeserializationError), - #[error("Corrupted data: {0}")] - CorruptedData(String), #[error("Invalid Felt: {0}")] InvalidFelt(String), #[error("Block applying was broken because of closed channel on state side: {0}")] @@ -91,6 +90,19 @@ impl From for DatabaseError { } } +impl From for Status { + fn from(err: DatabaseError) -> Self { + match err { + DatabaseError::AccountNotFoundInDb(_) + | DatabaseError::AccountsNotFoundInDb(_) + | DatabaseError::AccountNotOnChain(_) + | DatabaseError::BlockNotFoundInDb(_) => Status::not_found(err.to_string()), + + _ => Status::internal(err.to_string()), + } + } +} + // INITIALIZATION ERRORS // ================================================================================================= diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index 38e4bdaf..0018ff16 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -70,7 +70,7 @@ impl api_server::Api for StoreApi { )] async fn get_block_header_by_number( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { info!(target: COMPONENT, ?request); let request = request.into_inner(); @@ -102,7 +102,7 @@ impl api_server::Api for StoreApi { )] async fn check_nullifiers( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { // Validate the nullifiers and convert them to Digest values. Stop on first error. let request = request.into_inner(); @@ -126,7 +126,7 @@ impl api_server::Api for StoreApi { )] async fn check_nullifiers_by_prefix( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -137,8 +137,7 @@ impl api_server::Api for StoreApi { let nullifiers = self .state .check_nullifiers_by_prefix(request.prefix_len, request.nullifiers) - .await - .map_err(internal_error)? + .await? .into_iter() .map(|nullifier_info| NullifierUpdate { nullifier: Some(nullifier_info.nullifier.into()), @@ -160,7 +159,7 @@ impl api_server::Api for StoreApi { )] async fn sync_state( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -224,7 +223,7 @@ impl api_server::Api for StoreApi { )] async fn sync_notes( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -256,7 +255,7 @@ impl api_server::Api for StoreApi { )] async fn get_notes_by_id( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { info!(target: COMPONENT, ?request); @@ -270,8 +269,7 @@ impl api_server::Api for StoreApi { let notes = self .state .get_notes_by_id(note_ids) - .await - .map_err(internal_error)? + .await? .into_iter() .map(Into::into) .collect(); @@ -289,7 +287,7 @@ impl api_server::Api for StoreApi { )] async fn get_note_authentication_info( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { info!(target: COMPONENT, ?request); @@ -325,7 +323,7 @@ impl api_server::Api for StoreApi { )] async fn get_account_details( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); let account_info = self @@ -333,8 +331,7 @@ impl api_server::Api for StoreApi { .get_account_details( request.account_id.ok_or(invalid_argument("Account missing id"))?.into(), ) - .await - .map_err(internal_error)?; + .await?; Ok(Response::new(GetAccountDetailsResponse { details: Some((&account_info).into()), @@ -354,8 +351,8 @@ impl api_server::Api for StoreApi { )] async fn apply_block( &self, - request: tonic::Request, - ) -> Result, tonic::Status> { + request: Request, + ) -> Result, Status> { let request = request.into_inner(); debug!(target: COMPONENT, ?request); @@ -391,7 +388,7 @@ impl api_server::Api for StoreApi { )] async fn get_block_inputs( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -417,7 +414,7 @@ impl api_server::Api for StoreApi { )] async fn get_transaction_inputs( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -430,8 +427,7 @@ impl api_server::Api for StoreApi { let tx_inputs = self .state .get_transaction_inputs(account_id, &nullifiers, unauthenticated_notes) - .await - .map_err(internal_error)?; + .await?; let block_height = self.state.latest_block_num().await; @@ -466,13 +462,13 @@ impl api_server::Api for StoreApi { )] async fn get_block_by_number( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); debug!(target: COMPONENT, ?request); - let block = self.state.load_block(request.block_num).await.map_err(internal_error)?; + let block = self.state.load_block(request.block_num).await?; Ok(Response::new(GetBlockByNumberResponse { block })) } @@ -494,11 +490,8 @@ impl api_server::Api for StoreApi { let account_ids = convert(request.account_ids); let include_headers = request.include_headers.unwrap_or_default(); - let (block_num, infos) = self - .state - .get_account_states(account_ids, include_headers) - .await - .map_err(internal_error)?; + let (block_num, infos) = + self.state.get_account_proofs(account_ids, include_headers).await?; Ok(Response::new(GetAccountProofsResponse { block_num, @@ -515,7 +508,7 @@ impl api_server::Api for StoreApi { )] async fn get_account_state_delta( &self, - request: tonic::Request, + request: Request, ) -> Result, Status> { let request = request.into_inner(); @@ -528,8 +521,7 @@ impl api_server::Api for StoreApi { request.from_block_num, request.to_block_num, ) - .await - .map_err(internal_error)?; + .await?; Ok(Response::new(GetAccountStateDeltaResponse { delta: Some(delta.to_bytes()) })) } @@ -547,9 +539,9 @@ impl api_server::Api for StoreApi { )] async fn list_nullifiers( &self, - _request: tonic::Request, + _request: Request, ) -> Result, Status> { - let raw_nullifiers = self.state.list_nullifiers().await.map_err(internal_error)?; + let raw_nullifiers = self.state.list_nullifiers().await?; let nullifiers = raw_nullifiers .into_iter() .map(|(key, block_num)| SmtLeafEntry { @@ -570,16 +562,9 @@ impl api_server::Api for StoreApi { )] async fn list_notes( &self, - _request: tonic::Request, + _request: Request, ) -> Result, Status> { - let notes = self - .state - .list_notes() - .await - .map_err(internal_error)? - .into_iter() - .map(Into::into) - .collect(); + let notes = self.state.list_notes().await?.into_iter().map(Into::into).collect(); Ok(Response::new(ListNotesResponse { notes })) } @@ -593,16 +578,9 @@ impl api_server::Api for StoreApi { )] async fn list_accounts( &self, - _request: tonic::Request, + _request: Request, ) -> Result, Status> { - let accounts = self - .state - .list_accounts() - .await - .map_err(internal_error)? - .iter() - .map(Into::into) - .collect(); + let accounts = self.state.list_accounts().await?.iter().map(Into::into).collect(); Ok(Response::new(ListAccountsResponse { accounts })) } } @@ -610,13 +588,14 @@ impl api_server::Api for StoreApi { // UTILITIES // ================================================================================================ -/// Formats an error -fn internal_error(err: E) -> Status { - Status::internal(format!("{:?}", err)) +/// Formats an "Internal error" error +fn internal_error(err: E) -> Status { + Status::internal(err.to_string()) } -fn invalid_argument(err: E) -> Status { - Status::invalid_argument(format!("{:?}", err)) +/// Formats an "Invalid argument" error +fn invalid_argument(err: E) -> Status { + Status::invalid_argument(err.to_string()) } #[instrument(target = "miden-store", skip_all, err)] diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 44e03425..294f1ebc 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -679,8 +679,8 @@ impl State { self.db.select_account(id).await } - /// Returns account states with details for public accounts. - pub async fn get_account_states( + /// Returns account proofs with optional account and storage headers. + pub async fn get_account_proofs( &self, account_ids: Vec, include_headers: bool, From 40a42f11675193417949a0bef130a7f07ca269a3 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:23:06 +0500 Subject: [PATCH 02/29] docs: update `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ded380..88d847b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Added `GetAccountProofs` endpoint (#506). - Migrated faucet from actix-web to axum (#511). - Changed the `BlockWitness` to pass the inputs to the VM using only advice provider (#516). +- [BREAKING] Improved store API errors (return "not found" instead of "internal error" status if requested account(s) not found) (#518). ## 0.5.1 (2024-09-12) From 8873ba59e703b69f494941fe0d544983437b1886 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:45:29 +0500 Subject: [PATCH 03/29] feat: save/restore faucet state and keypair to/from files --- Cargo.lock | 13 ++ bin/faucet/Cargo.toml | 5 +- bin/faucet/src/client.rs | 253 +++++++++++++++++++------------------ bin/faucet/src/config.rs | 19 ++- bin/faucet/src/errors.rs | 39 +++--- bin/faucet/src/handlers.rs | 35 +++-- bin/faucet/src/main.rs | 43 +++---- bin/faucet/src/state.rs | 9 +- bin/faucet/src/store.rs | 182 ++++++++++++++++++++++++++ 9 files changed, 414 insertions(+), 184 deletions(-) create mode 100644 bin/faucet/src/store.rs diff --git a/Cargo.lock b/Cargo.lock index e110537a..9958a561 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,6 +191,7 @@ checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bytes", "futures-util", "http", @@ -238,6 +239,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1466,6 +1478,7 @@ dependencies = [ name = "miden-faucet" version = "0.6.0" dependencies = [ + "anyhow", "axum", "clap", "figment", diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index fc03a81a..b4fa4715 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -17,7 +17,8 @@ repository.workspace = true testing = ["miden-objects/testing", "miden-lib/testing"] [dependencies] -axum = { version = "0.7", features = ["tokio"] } +anyhow = "1.0" +axum = { version = "0.7", features = ["tokio", "macros"] } clap = { version = "4.5", features = ["derive", "string"] } figment = { version = "0.10", features = ["toml", "env"] } http = "1.1" @@ -33,7 +34,7 @@ rand_chacha = "0.3" serde = { version = "1.0", features = ["derive"] } static-files = "0.2" thiserror = { workspace = true } -tokio = { workspace = true } +tokio = { workspace = true, features = ["fs"] } toml = { version = "0.8" } tonic = { workspace = true } tower = "0.5" diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index d02f4a13..cddfa2be 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -1,30 +1,39 @@ -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{ + path::Path, + rc::Rc, + sync::{Arc, RwLock}, + time::Duration, +}; +use anyhow::{anyhow, Context}; use miden_lib::{ accounts::faucets::create_basic_fungible_faucet, notes::create_p2id_note, transaction::TransactionKernel, AuthScheme, }; use miden_node_proto::generated::{ - requests::{GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest}, + requests::{ + GetAccountProofsRequest, GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, + }, rpc::api_client::ApiClient, }; use miden_objects::{ - accounts::{Account, AccountDelta, AccountId, AccountStorageMode, AuthSecretKey}, + accounts::{Account, AccountId, AccountStorageMode, AuthSecretKey}, assets::{FungibleAsset, TokenSymbol}, crypto::{ dsa::rpo_falcon512::SecretKey, + hash::rpo::RpoDigest, merkle::{MmrPeaks, PartialMmr}, rand::RpoRandomCoin, }, - notes::{Note, NoteId, NoteType}, - transaction::{ChainMmr, ExecutedTransaction, InputNotes, TransactionArgs, TransactionScript}, + notes::{Note, NoteType}, + transaction::{ChainMmr, ExecutedTransaction, TransactionArgs, TransactionScript}, + utils::Deserializable, vm::AdviceMap, BlockHeader, Felt, Word, }; use miden_tx::{ - auth::BasicAuthenticator, utils::Serializable, DataStore, DataStoreError, - LocalTransactionProver, ProvingOptions, TransactionExecutor, TransactionInputs, - TransactionProver, + auth::BasicAuthenticator, utils::Serializable, LocalTransactionProver, ProvingOptions, + TransactionExecutor, TransactionProver, }; use rand::{rngs::StdRng, thread_rng, Rng}; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; @@ -32,7 +41,8 @@ use tonic::transport::Channel; use crate::{ config::FaucetConfig, - errors::{InitError, ProcessError}, + errors::{ErrorHelper, HandlerError}, + store::{resolve_faucet_state, FaucetDataStore}, }; pub const DISTRIBUTE_FUNGIBLE_ASSET_SCRIPT: &str = @@ -52,27 +62,51 @@ pub struct FaucetClient { } unsafe impl Send for FaucetClient {} -unsafe impl Sync for FaucetClient {} impl FaucetClient { - pub async fn new(config: FaucetConfig) -> Result { - let (rpc_api, root_block_header, root_chain_mmr) = - initialize_faucet_client(config.clone()).await?; + /// Creates a new faucet client. + pub async fn new(config: &FaucetConfig) -> anyhow::Result { + let (mut rpc_api, root_block_header, root_chain_mmr) = + initialize_faucet_client(config).await?; + let init_seed: [u8; 32] = [0; 32]; + let (auth_scheme, authenticator) = init_authenticator(init_seed, &config.secret_key_path) + .context("Failed to initialize authentication scheme")?; + + let (mut faucet_account, account_seed) = build_account(config, init_seed, auth_scheme)?; + let id = faucet_account.id(); + + let faucet_actual_hash: RpoDigest = rpc_api + .get_account_proofs(GetAccountProofsRequest { + account_ids: vec![id.into()], + include_headers: None, + }) + .await + .context("Failed to get faucet account proof")? + .into_inner() + .account_proofs + .first() + .context("No account proofs returned by RPC")? + .account_hash + .context("Account hash field is empty")? + .try_into() + .context("Failed to convert hash from protobuf to `RpoDigest`")?; + + if faucet_account.hash() != faucet_actual_hash { + faucet_account = resolve_faucet_state(&config.storage_path, faucet_actual_hash) + .await + .context("Failed to restore faucet state from files")?; + } - let (faucet_account, account_seed, secret) = build_account(config.clone())?; - let faucet_account = Rc::new(RefCell::new(faucet_account)); - let id = faucet_account.borrow().id(); + let faucet_account = Arc::new(RwLock::new(faucet_account)); let data_store = FaucetDataStore::new( - faucet_account.clone(), + config.storage_path.clone(), + Arc::clone(&faucet_account), account_seed, root_block_header, root_chain_mmr, ); - let authenticator = BasicAuthenticator::::new(&[( - secret.public_key().into(), - AuthSecretKey::RpoFalcon512(secret), - )]); + let executor = TransactionExecutor::new(data_store.clone(), Some(Rc::new(authenticator))); let mut rng = thread_rng(); @@ -90,9 +124,9 @@ impl FaucetClient { target_account_id: AccountId, is_private_note: bool, asset_amount: u64, - ) -> Result<(ExecutedTransaction, Note), ProcessError> { - let asset = FungibleAsset::new(self.id, asset_amount) - .map_err(|err| ProcessError::InternalServerError(err.to_string()))?; + ) -> Result<(ExecutedTransaction, Note), HandlerError> { + let asset = + FungibleAsset::new(self.id, asset_amount).or_fail("Failed to create fungible asset")?; let note_type = if is_private_note { NoteType::Private @@ -108,16 +142,14 @@ impl FaucetClient { Default::default(), &mut self.rng, ) - .map_err(|err| ProcessError::InternalServerError(err.to_string()))?; + .or_fail("Failed to create P2ID note")?; let transaction_args = build_transaction_arguments(&output_note, note_type, asset)?; let executed_tx = self .executor .execute_transaction(self.id, 0, &[], transaction_args) - .map_err(|err| { - ProcessError::InternalServerError(format!("Failed to execute transaction: {err}")) - })?; + .or_fail("Failed to execute transaction")?; Ok((executed_tx, output_note)) } @@ -126,18 +158,15 @@ impl FaucetClient { pub async fn prove_and_submit_transaction( &mut self, executed_tx: ExecutedTransaction, - ) -> Result { - let delta = executed_tx.account_delta().clone(); - + ) -> Result { // Prepare request with proven transaction. // This is needed to be in a separated code block in order to release reference to avoid // borrow checker error. let request = { let transaction_prover = LocalTransactionProver::new(ProvingOptions::default()); - let proven_transaction = transaction_prover.prove(executed_tx).map_err(|err| { - ProcessError::InternalServerError(format!("Failed to prove transaction: {err}")) - })?; + let proven_transaction = + transaction_prover.prove(executed_tx).or_fail("Failed to prove transaction")?; SubmitProvenTransactionRequest { transaction: proven_transaction.to_bytes(), @@ -148,137 +177,113 @@ impl FaucetClient { .rpc_api .submit_proven_transaction(request) .await - .map_err(|err| ProcessError::InternalServerError(err.to_string()))?; - - self.data_store.update_faucet_account(&delta).map_err(|err| { - ProcessError::InternalServerError(format!("Failed to update account: {err}")) - })?; + .or_fail("Failed to submit proven transaction")?; Ok(response.into_inner().block_height) } + /// Returns a reference to the data store. + pub fn data_store(&self) -> &FaucetDataStore { + &self.data_store + } + + /// Returns a mutable reference to the data store. + pub fn data_store_mut(&mut self) -> &mut FaucetDataStore { + &mut self.data_store + } + + /// Returns the id of the faucet account. pub fn get_faucet_id(&self) -> AccountId { self.id } } -#[derive(Clone)] -pub struct FaucetDataStore { - faucet_account: Rc>, - seed: Word, - block_header: BlockHeader, - chain_mmr: ChainMmr, -} - -// FAUCET DATA STORE +// HELPER FUNCTIONS // ================================================================================================ -impl FaucetDataStore { - pub fn new( - faucet_account: Rc>, - seed: Word, - root_block_header: BlockHeader, - root_chain_mmr: ChainMmr, - ) -> Self { - Self { - faucet_account, - seed, - block_header: root_block_header, - chain_mmr: root_chain_mmr, - } - } - - /// Updates the stored faucet account with the provided delta. - fn update_faucet_account(&mut self, delta: &AccountDelta) -> Result<(), ProcessError> { - self.faucet_account - .borrow_mut() - .apply_delta(delta) - .map_err(|err| ProcessError::InternalServerError(err.to_string())) - } -} +/// Initializes the keypair used to sign transactions. +/// +/// If the secret key file exists, it is read from the file. Otherwise, a new key is generated and +/// written to the file. +fn init_authenticator( + init_seed: [u8; 32], + secret_key_path: impl AsRef, +) -> anyhow::Result<(AuthScheme, BasicAuthenticator)> { + // Load secret key from file or generate new one + let secret = if secret_key_path.as_ref().exists() { + SecretKey::read_from_bytes( + &std::fs::read(secret_key_path).context("Failed to read secret key from file")?, + ) + .map_err(|err| anyhow!("Failed to deserialize secret key: {err}"))? + } else { + let mut rng = ChaCha20Rng::from_seed(init_seed); + let secret = SecretKey::with_rng(&mut rng); + std::fs::write(secret_key_path, secret.to_bytes()) + .context("Failed to write secret key to file")?; + + secret + }; -impl DataStore for FaucetDataStore { - fn get_transaction_inputs( - &self, - account_id: AccountId, - _block_ref: u32, - _notes: &[NoteId], - ) -> Result { - let account = self.faucet_account.borrow(); - if account_id != account.id() { - return Err(DataStoreError::AccountNotFound(account_id)); - } + let auth_scheme = AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }; - let empty_input_notes = - InputNotes::new(Vec::new()).map_err(DataStoreError::InvalidTransactionInput)?; + let authenticator = BasicAuthenticator::::new(&[( + secret.public_key().into(), + AuthSecretKey::RpoFalcon512(secret), + )]); - TransactionInputs::new( - account.clone(), - account.is_new().then_some(self.seed), - self.block_header, - self.chain_mmr.clone(), - empty_input_notes, - ) - .map_err(DataStoreError::InvalidTransactionInput) - } + Ok((auth_scheme, authenticator)) } -// HELPER FUNCTIONS -// ================================================================================================ - /// Builds a new faucet account with the provided configuration. /// /// Returns the created account, its seed, and the secret key used to sign transactions. -fn build_account(config: FaucetConfig) -> Result<(Account, Word, SecretKey), InitError> { +fn build_account( + config: &FaucetConfig, + init_seed: [u8; 32], + auth_scheme: AuthScheme, +) -> anyhow::Result<(Account, Word)> { let token_symbol = TokenSymbol::new(config.token_symbol.as_str()) - .map_err(|err| InitError::AccountCreationError(err.to_string()))?; - - let seed: [u8; 32] = [0; 32]; - - // Instantiate keypair and auth scheme - let mut rng = ChaCha20Rng::from_seed(seed); - let secret = SecretKey::with_rng(&mut rng); - let auth_scheme = AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }; + .context("Failed to parse token symbol from configuration file")?; let (faucet_account, account_seed) = create_basic_fungible_faucet( - seed, + init_seed, token_symbol, config.decimals, Felt::try_from(config.max_supply) - .map_err(|err| InitError::AccountCreationError(err.to_string()))?, + .map_err(|err| anyhow!("Error converting max supply to Felt: {err}"))?, AccountStorageMode::Private, auth_scheme, ) - .map_err(|err| InitError::AccountCreationError(err.to_string()))?; + .context("Failed to create basic fungible faucet account")?; - Ok((faucet_account, account_seed, secret)) + Ok((faucet_account, account_seed)) } /// Initializes the faucet client by connecting to the node and fetching the root block header. pub async fn initialize_faucet_client( - config: FaucetConfig, -) -> Result<(ApiClient, BlockHeader, ChainMmr), InitError> { + config: &FaucetConfig, +) -> anyhow::Result<(ApiClient, BlockHeader, ChainMmr)> { let endpoint = tonic::transport::Endpoint::try_from(config.node_url.clone()) - .map_err(|_| InitError::ClientInitFailed("Failed to connect to node.".to_string()))? + .context("Failed to parse node URL from configuration file")? .timeout(Duration::from_millis(config.timeout_ms)); - let mut rpc_api = ApiClient::connect(endpoint) - .await - .map_err(|err| InitError::ClientInitFailed(err.to_string()))?; + let mut rpc_api = + ApiClient::connect(endpoint).await.context("Failed to connect to the node")?; let request = GetBlockHeaderByNumberRequest { block_num: Some(0), - include_mmr_proof: Some(true), + include_mmr_proof: None, }; let response = rpc_api .get_block_header_by_number(request) .await - .map_err(|err| InitError::ClientInitFailed(format!("Failed to get block header: {err}")))?; - let root_block_header = response.into_inner().block_header.unwrap(); + .context("Failed to get block header")?; + let root_block_header = response + .into_inner() + .block_header + .context("Missing root block header in response")?; - let root_block_header: BlockHeader = root_block_header.try_into().map_err(|err| { - InitError::ClientInitFailed(format!("Failed to parse block header: {err}")) - })?; + let root_block_header = root_block_header.try_into().context("Failed to parse block header")?; let root_chain_mmr = ChainMmr::new( PartialMmr::from_peaks( @@ -296,7 +301,7 @@ fn build_transaction_arguments( output_note: &Note, note_type: NoteType, asset: FungibleAsset, -) -> Result { +) -> Result { let recipient = output_note .recipient() .digest() @@ -318,9 +323,7 @@ fn build_transaction_arguments( .replace("{execution_hint}", &Felt::new(execution_hint).to_string()); let script = TransactionScript::compile(script, vec![], TransactionKernel::assembler()) - .map_err(|err| { - ProcessError::InternalServerError(format!("Failed to compile script: {err}")) - })?; + .or_fail("Failed to compile script")?; let mut transaction_args = TransactionArgs::new(Some(script), None, AdviceMap::new()); transaction_args.extend_expected_output_notes(vec![output_note.clone()]); diff --git a/bin/faucet/src/config.rs b/bin/faucet/src/config.rs index 1e443585..9159752d 100644 --- a/bin/faucet/src/config.rs +++ b/bin/faucet/src/config.rs @@ -1,4 +1,7 @@ -use std::fmt::{Display, Formatter}; +use std::{ + fmt::{Display, Formatter}, + path::PathBuf, +}; use miden_node_utils::config::{Endpoint, DEFAULT_FAUCET_SERVER_PORT, DEFAULT_NODE_RPC_PORT}; use serde::{Deserialize, Serialize}; @@ -6,12 +9,18 @@ use serde::{Deserialize, Serialize}; // Faucet config // ================================================================================================ +/// Default path to the secret key file +const DEFAULT_SECRET_KEY_PATH: &str = "faucet-secret.key"; + +/// Default path to the storage directory +const DEFAULT_STORAGE_PATH: &str = "faucet-storage"; + #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct FaucetConfig { /// Endpoint of the faucet pub endpoint: Endpoint, - /// Node RPC gRPC endpoint in the format `http://[:]`. + /// Node RPC gRPC endpoint in the format `http://[:]` pub node_url: String, /// Timeout for RPC requests in milliseconds pub timeout_ms: u64, @@ -23,6 +32,10 @@ pub struct FaucetConfig { pub decimals: u8, /// Maximum supply of the generated fungible asset pub max_supply: u64, + /// Path to the key store file + pub secret_key_path: PathBuf, + /// Path to the storage directory + pub storage_path: PathBuf, } impl Display for FaucetConfig { @@ -44,6 +57,8 @@ impl Default for FaucetConfig { token_symbol: "POL".to_string(), decimals: 8, max_supply: 1000000, + secret_key_path: DEFAULT_SECRET_KEY_PATH.into(), + storage_path: DEFAULT_STORAGE_PATH.into(), } } } diff --git a/bin/faucet/src/errors.rs b/bin/faucet/src/errors.rs index 50777c71..029094e9 100644 --- a/bin/faucet/src/errors.rs +++ b/bin/faucet/src/errors.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use axum::{ http::{header, StatusCode}, response::{IntoResponse, Response}, @@ -5,22 +7,7 @@ use axum::{ use thiserror::Error; #[derive(Debug, Error)] -pub enum InitError { - #[error("Failed to start faucet: {0}")] - FaucetFailedToStart(String), - - #[error("Failed to initialize client: {0}")] - ClientInitFailed(String), - - #[error("Failed to configure faucet: {0}")] - ConfigurationError(String), - - #[error("Failed to create Miden account: {0}")] - AccountCreationError(String), -} - -#[derive(Debug, Error)] -pub enum ProcessError { +pub enum HandlerError { #[error("Client has submitted a bad request: {0}")] BadRequest(String), @@ -31,7 +18,7 @@ pub enum ProcessError { NotFound(String), } -impl ProcessError { +impl HandlerError { fn status_code(&self) -> StatusCode { match *self { Self::BadRequest(_) => StatusCode::BAD_REQUEST, @@ -50,7 +37,7 @@ impl ProcessError { } } -impl IntoResponse for ProcessError { +impl IntoResponse for HandlerError { fn into_response(self) -> Response { ( self.status_code(), @@ -60,3 +47,19 @@ impl IntoResponse for ProcessError { .into_response() } } + +pub trait ErrorHelper { + fn or_fail(self, message: impl Display) -> Result; +} + +impl ErrorHelper for Result { + fn or_fail(self, message: impl Display) -> Result { + self.map_err(|err| HandlerError::InternalServerError(format!("{message}: {err}"))) + } +} + +impl ErrorHelper for Option { + fn or_fail(self, message: impl Display) -> Result { + self.ok_or_else(|| HandlerError::InternalServerError(message.to_string())) + } +} diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index 5e51e4fd..a3a49037 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -1,4 +1,5 @@ use axum::{ + debug_handler, extract::{Path, State}, http::{Response, StatusCode}, response::IntoResponse, @@ -15,7 +16,11 @@ use serde::{Deserialize, Serialize}; use tonic::body; use tracing::info; -use crate::{errors::ProcessError, state::FaucetState, COMPONENT}; +use crate::{ + errors::{ErrorHelper, HandlerError}, + state::FaucetState, + COMPONENT, +}; #[derive(Deserialize)] pub struct FaucetRequest { @@ -41,10 +46,11 @@ pub async fn get_metadata( (StatusCode::OK, Json(response)) } +#[debug_handler] pub async fn get_tokens( State(state): State, Json(req): Json, -) -> Result { +) -> Result { info!( target: COMPONENT, account_id = %req.account_id, @@ -55,14 +61,14 @@ pub async fn get_tokens( // Check that the amount is in the asset amount options if !state.config.asset_amount_options.contains(&req.asset_amount) { - return Err(ProcessError::BadRequest("Invalid asset amount".to_string())); + return Err(HandlerError::BadRequest("Invalid asset amount".to_string())); } let mut client = state.client.lock().await; // Receive and hex user account id let target_account_id = AccountId::from_hex(req.account_id.as_str()) - .map_err(|err| ProcessError::BadRequest(err.to_string()))?; + .map_err(|err| HandlerError::BadRequest(err.to_string()))?; // Execute transaction info!(target: COMPONENT, "Executing mint transaction for account."); @@ -72,16 +78,25 @@ pub async fn get_tokens( req.asset_amount, )?; + let mut updated_faucet_account = client.data_store().faucet_account(); + updated_faucet_account + .apply_delta(executed_tx.account_delta()) + .or_fail("Failed to apply faucet account delta")?; + + client.data_store().save_next_faucet_state(&updated_faucet_account).await?; + // Run transaction prover & send transaction to node info!(target: COMPONENT, "Proving and submitting transaction."); let block_height = client.prove_and_submit_transaction(executed_tx).await?; + client.data_store_mut().update_faucet_state(updated_faucet_account).await?; + let note_id: NoteId = created_note.id(); let note_details = NoteDetails::new(created_note.assets().clone(), created_note.recipient().clone()); let note_tag = NoteTag::from_account_id(target_account_id, NoteExecutionMode::Local) - .expect("failed to build note tag for local execution"); + .or_fail("failed to build note tag for local execution")?; // Serialize note into bytes let bytes = NoteFile::NoteDetails { @@ -100,24 +115,24 @@ pub async fn get_tokens( .header(header::CONTENT_DISPOSITION, "attachment; filename=note.mno") .header("Note-Id", note_id.to_string()) .body(body::boxed(Full::from(bytes))) - .map_err(|err| ProcessError::InternalServerError(err.to_string())) + .or_fail("Failed to build response") } -pub async fn get_index(state: State) -> Result { +pub async fn get_index(state: State) -> Result { get_static_file(state, Path("index.html".to_string())).await } pub async fn get_static_file( State(state): State, Path(path): Path, -) -> Result { +) -> Result { info!(target: COMPONENT, path, "Serving static file"); - let static_file = state.static_files.get(path.as_str()).ok_or(ProcessError::NotFound(path))?; + let static_file = state.static_files.get(path.as_str()).ok_or(HandlerError::NotFound(path))?; Response::builder() .status(StatusCode::OK) .header(header::CONTENT_TYPE, static_file.mime_type) .body(body::boxed(Full::from(static_file.data))) - .map_err(|err| ProcessError::InternalServerError(err.to_string())) + .or_fail("Failed to build response") } diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index f58f9f18..2188d4c5 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -3,9 +3,11 @@ mod config; mod errors; mod handlers; mod state; +mod store; use std::{fs::File, io::Write, path::PathBuf}; +use anyhow::Context; use axum::{ routing::{get, post}, Router, @@ -21,7 +23,6 @@ use tracing::info; use crate::{ config::FaucetConfig, - errors::InitError, handlers::{get_index, get_metadata, get_static_file, get_tokens}, }; // CONSTANTS @@ -59,16 +60,15 @@ pub enum Command { // ================================================================================================= #[tokio::main] -async fn main() -> Result<(), InitError> { - miden_node_utils::logging::setup_logging() - .map_err(|err| InitError::FaucetFailedToStart(err.to_string()))?; +async fn main() -> anyhow::Result<()> { + miden_node_utils::logging::setup_logging().context("Failed to initialize logging")?; let cli = Cli::parse(); match &cli.command { Command::Start { config } => { - let config: FaucetConfig = load_config(config) - .map_err(|err| InitError::ConfigurationError(err.to_string()))?; + let config: FaucetConfig = + load_config(config).context("Failed to load configuration file")?; let faucet_state = FaucetState::new(config.clone()).await?; @@ -96,31 +96,30 @@ async fn main() -> Result<(), InitError> { let listener = TcpListener::bind((config.endpoint.host.as_str(), config.endpoint.port)) .await - .map_err(|err| InitError::FaucetFailedToStart(err.to_string()))?; + .context("Failed to bind TCP listener")?; info!(target: COMPONENT, endpoint = %config.endpoint, "Server started"); axum::serve(listener, app).await.unwrap(); }, Command::Init { config_path } => { - let current_dir = std::env::current_dir().map_err(|err| { - InitError::ConfigurationError(format!("failed to open current directory: {err}")) - })?; + let current_dir = + std::env::current_dir().context("failed to open current directory")?; let config_file_path = current_dir.join(config_path); let config = FaucetConfig::default(); - let config_as_toml_string = toml::to_string(&config).map_err(|err| { - InitError::ConfigurationError(format!("Failed to serialize default config: {err}")) - })?; - - let mut file_handle = - File::options().write(true).create_new(true).open(&config_file_path).map_err( - |err| InitError::ConfigurationError(format!("Error opening the file: {err}")), - )?; - - file_handle.write(config_as_toml_string.as_bytes()).map_err(|err| { - InitError::ConfigurationError(format!("Error writing to file: {err}")) - })?; + let config_as_toml_string = + toml::to_string(&config).context("Failed to serialize default config")?; + + let mut file_handle = File::options() + .write(true) + .create_new(true) + .open(&config_file_path) + .context("Error opening configuration file")?; + + file_handle + .write(config_as_toml_string.as_bytes()) + .context("Error writing to file")?; println!("Config file successfully created at: {config_file_path:?}"); }, diff --git a/bin/faucet/src/state.rs b/bin/faucet/src/state.rs index 898a5e7b..fb694081 100644 --- a/bin/faucet/src/state.rs +++ b/bin/faucet/src/state.rs @@ -5,9 +5,8 @@ use static_files::Resource; use tokio::sync::Mutex; use tracing::info; -use crate::{ - client::FaucetClient, config::FaucetConfig, errors::InitError, static_resources, COMPONENT, -}; +use crate::{client::FaucetClient, config::FaucetConfig, static_resources, COMPONENT}; + // FAUCET STATE // ================================================================================================ @@ -24,8 +23,8 @@ pub struct FaucetState { } impl FaucetState { - pub async fn new(config: FaucetConfig) -> Result { - let client = FaucetClient::new(config.clone()).await?; + pub async fn new(config: FaucetConfig) -> anyhow::Result { + let client = FaucetClient::new(&config).await?; let id = client.get_faucet_id(); let client = Arc::new(Mutex::new(client)); let static_files = Arc::new(static_resources::generate()); diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs new file mode 100644 index 00000000..5905a27d --- /dev/null +++ b/bin/faucet/src/store.rs @@ -0,0 +1,182 @@ +use std::{ + path::{Path, PathBuf}, + sync::{Arc, RwLock}, +}; + +use anyhow::{bail, Context}; +use miden_objects::{ + accounts::{Account, AccountId}, + crypto::hash::rpo::RpoDigest, + notes::NoteId, + transaction::{ChainMmr, InputNotes, TransactionInputs}, + utils::{Deserializable, Serializable}, + BlockHeader, Word, +}; +use miden_tx::{DataStore, DataStoreError}; + +use crate::errors::{ErrorHelper, HandlerError}; + +const FAUCET_ACCOUNT_FILENAME: &str = "faucet-account.bin"; + +#[derive(Clone)] +pub struct FaucetDataStore { + storage_path: PathBuf, + faucet_account: Arc>, + seed: Word, + block_header: BlockHeader, + chain_mmr: ChainMmr, +} + +// FAUCET DATA STORE +// ================================================================================================ + +impl FaucetDataStore { + pub fn new( + storage_path: PathBuf, + faucet_account: Arc>, + seed: Word, + root_block_header: BlockHeader, + root_chain_mmr: ChainMmr, + ) -> Self { + Self { + storage_path, + faucet_account, + seed, + block_header: root_block_header, + chain_mmr: root_chain_mmr, + } + } + + /// Returns the stored faucet account. + pub fn faucet_account(&self) -> Account { + self.faucet_account.read().expect("Poisoned lock").clone() + } + + /// Saves the next faucet state to file. + pub async fn save_next_faucet_state( + &self, + next_faucet_state: &Account, + ) -> Result<(), HandlerError> { + tokio::fs::create_dir_all(&self.storage_path) + .await + .or_fail("Failed to create directory for storage")?; + + tokio::fs::write(next_faucet_state_path(&self.storage_path), next_faucet_state.to_bytes()) + .await + .or_fail("Failed to save faucet account to file") + } + + /// Switches file storage to the next faucet state. + pub async fn switch_to_next_faucet_state(&self) -> Result<(), HandlerError> { + tokio::fs::rename( + next_faucet_state_path(&self.storage_path), + faucet_state_path(&self.storage_path), + ) + .await + .or_fail("Failed to rename next faucet account file to the current one") + } + + /// Updates the stored faucet account with the new one. + pub async fn update_faucet_state( + &mut self, + new_faucet_state: Account, + ) -> Result<(), HandlerError> { + self.switch_to_next_faucet_state().await?; + *self.faucet_account.write().expect("Poisoned lock") = new_faucet_state; + + Ok(()) + } +} + +impl DataStore for FaucetDataStore { + fn get_transaction_inputs( + &self, + account_id: AccountId, + _block_ref: u32, + _notes: &[NoteId], + ) -> Result { + let account = self.faucet_account.read().expect("Poisoned lock"); + if account_id != account.id() { + return Err(DataStoreError::AccountNotFound(account_id)); + } + + let empty_input_notes = + InputNotes::new(Vec::new()).map_err(DataStoreError::InvalidTransactionInput)?; + + TransactionInputs::new( + account.clone(), + account.is_new().then_some(self.seed), + self.block_header, + self.chain_mmr.clone(), + empty_input_notes, + ) + .map_err(DataStoreError::InvalidTransactionInput) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Tries to restore the faucet state from current and/or next file(s). +/// +/// If the faucet state with the expected hash is not found in either file, an error is returned. +pub async fn resolve_faucet_state( + storage_path: impl AsRef, + expected_hash: RpoDigest, +) -> anyhow::Result { + if !faucet_state_path(&storage_path).exists() { + bail!("Faucet state file does not exist"); + } + + let current_state = load_current_faucet_state(&storage_path) + .await + .context("Failed to restore current faucet state")?; + + if current_state.hash() == expected_hash { + return Ok(current_state); + } + + if !next_faucet_state_path(&storage_path).exists() { + bail!("Next faucet state file does not exist"); + } + + let next_state = load_next_faucet_state(&storage_path) + .await + .context("Failed to restore next faucet state")?; + + if next_state.hash() == expected_hash { + return Ok(next_state); + } + + bail!( + "Failed to restore faucet state from files. Could not find file with expected state hash." + ); +} + +/// Loads the current faucet state from file. +pub async fn load_current_faucet_state(storage_path: impl AsRef) -> anyhow::Result { + load_faucet_state_internal(faucet_state_path(storage_path)).await +} + +/// Loads the next faucet state from file. +pub async fn load_next_faucet_state(storage_path: impl AsRef) -> anyhow::Result { + load_faucet_state_internal(next_faucet_state_path(storage_path)).await +} + +/// Loads the faucet state from file with the given path. +async fn load_faucet_state_internal(path: impl AsRef) -> anyhow::Result { + let bytes = tokio::fs::read(path).await.context("Failed to read faucet account from file")?; + + Account::read_from_bytes(&bytes) + .map_err(|err| anyhow::anyhow!("Failed to deserialize faucet account from bytes: {err}")) +} + +/// Returns path to the faucet state file. +fn faucet_state_path(storage_path: impl AsRef) -> PathBuf { + storage_path.as_ref().join(FAUCET_ACCOUNT_FILENAME) +} + +/// Returns path to the next faucet state file. +fn next_faucet_state_path(storage_path: impl AsRef) -> PathBuf { + faucet_state_path(storage_path).with_extension("next") +} From 9053f12fafb62a57db9b3651049913e5d6221ef7 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Thu, 17 Oct 2024 20:23:59 +0500 Subject: [PATCH 04/29] feat: make faucet account public, request account state on submission error --- bin/faucet/src/client.rs | 70 +++++++++++------------ bin/faucet/src/config.rs | 6 -- bin/faucet/src/handlers.rs | 81 +++++++++++++++++++-------- bin/faucet/src/store.rs | 112 +------------------------------------ 4 files changed, 94 insertions(+), 175 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index cddfa2be..4be2cbba 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -12,7 +12,7 @@ use miden_lib::{ }; use miden_node_proto::generated::{ requests::{ - GetAccountProofsRequest, GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, + GetAccountDetailsRequest, GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, }, rpc::api_client::ApiClient, }; @@ -21,7 +21,6 @@ use miden_objects::{ assets::{FungibleAsset, TokenSymbol}, crypto::{ dsa::rpo_falcon512::SecretKey, - hash::rpo::RpoDigest, merkle::{MmrPeaks, PartialMmr}, rand::RpoRandomCoin, }, @@ -42,7 +41,7 @@ use tonic::transport::Channel; use crate::{ config::FaucetConfig, errors::{ErrorHelper, HandlerError}, - store::{resolve_faucet_state, FaucetDataStore}, + store::FaucetDataStore, }; pub const DISTRIBUTE_FUNGIBLE_ASSET_SCRIPT: &str = @@ -66,42 +65,16 @@ unsafe impl Send for FaucetClient {} impl FaucetClient { /// Creates a new faucet client. pub async fn new(config: &FaucetConfig) -> anyhow::Result { - let (mut rpc_api, root_block_header, root_chain_mmr) = - initialize_faucet_client(config).await?; + let (rpc_api, root_block_header, root_chain_mmr) = initialize_faucet_client(config).await?; let init_seed: [u8; 32] = [0; 32]; let (auth_scheme, authenticator) = init_authenticator(init_seed, &config.secret_key_path) .context("Failed to initialize authentication scheme")?; - let (mut faucet_account, account_seed) = build_account(config, init_seed, auth_scheme)?; + let (faucet_account, account_seed) = build_account(config, init_seed, auth_scheme)?; let id = faucet_account.id(); - let faucet_actual_hash: RpoDigest = rpc_api - .get_account_proofs(GetAccountProofsRequest { - account_ids: vec![id.into()], - include_headers: None, - }) - .await - .context("Failed to get faucet account proof")? - .into_inner() - .account_proofs - .first() - .context("No account proofs returned by RPC")? - .account_hash - .context("Account hash field is empty")? - .try_into() - .context("Failed to convert hash from protobuf to `RpoDigest`")?; - - if faucet_account.hash() != faucet_actual_hash { - faucet_account = resolve_faucet_state(&config.storage_path, faucet_actual_hash) - .await - .context("Failed to restore faucet state from files")?; - } - - let faucet_account = Arc::new(RwLock::new(faucet_account)); - let data_store = FaucetDataStore::new( - config.storage_path.clone(), - Arc::clone(&faucet_account), + Arc::new(RwLock::new(faucet_account)), account_seed, root_block_header, root_chain_mmr, @@ -182,16 +155,37 @@ impl FaucetClient { Ok(response.into_inner().block_height) } + /// Requests faucet account state from the node. + /// + /// The account is expected to be public, otherwise, the error is returned. + pub async fn request_account_state(&mut self) -> Result<(Account, u32), HandlerError> { + let account_info = self + .rpc_api + .get_account_details(GetAccountDetailsRequest { account_id: Some(self.id.into()) }) + .await + .or_fail("Failed to get faucet account state")? + .into_inner() + .details + .or_fail("Account info field is empty")?; + + let faucet_account_state_bytes = + account_info.details.or_fail("Account details field is empty")?; + let faucet_account = + Account::read_from_bytes(&faucet_account_state_bytes).map_err(|err| { + HandlerError::InternalServerError(format!( + "Failed to deserialize faucet account: {err}" + )) + })?; + let block_num = account_info.summary.or_fail("Account summary field is empty")?.block_num; + + Ok((faucet_account, block_num)) + } + /// Returns a reference to the data store. pub fn data_store(&self) -> &FaucetDataStore { &self.data_store } - /// Returns a mutable reference to the data store. - pub fn data_store_mut(&mut self) -> &mut FaucetDataStore { - &mut self.data_store - } - /// Returns the id of the faucet account. pub fn get_faucet_id(&self) -> AccountId { self.id @@ -251,7 +245,7 @@ fn build_account( config.decimals, Felt::try_from(config.max_supply) .map_err(|err| anyhow!("Error converting max supply to Felt: {err}"))?, - AccountStorageMode::Private, + AccountStorageMode::Public, auth_scheme, ) .context("Failed to create basic fungible faucet account")?; diff --git a/bin/faucet/src/config.rs b/bin/faucet/src/config.rs index 9159752d..631f1df9 100644 --- a/bin/faucet/src/config.rs +++ b/bin/faucet/src/config.rs @@ -12,9 +12,6 @@ use serde::{Deserialize, Serialize}; /// Default path to the secret key file const DEFAULT_SECRET_KEY_PATH: &str = "faucet-secret.key"; -/// Default path to the storage directory -const DEFAULT_STORAGE_PATH: &str = "faucet-storage"; - #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct FaucetConfig { @@ -34,8 +31,6 @@ pub struct FaucetConfig { pub max_supply: u64, /// Path to the key store file pub secret_key_path: PathBuf, - /// Path to the storage directory - pub storage_path: PathBuf, } impl Display for FaucetConfig { @@ -58,7 +53,6 @@ impl Default for FaucetConfig { decimals: 8, max_supply: 1000000, secret_key_path: DEFAULT_SECRET_KEY_PATH.into(), - storage_path: DEFAULT_STORAGE_PATH.into(), } } } diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index a3a49037..8bd31597 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -1,5 +1,4 @@ use axum::{ - debug_handler, extract::{Path, State}, http::{Response, StatusCode}, response::IntoResponse, @@ -14,7 +13,7 @@ use miden_objects::{ }; use serde::{Deserialize, Serialize}; use tonic::body; -use tracing::info; +use tracing::{error, info}; use crate::{ errors::{ErrorHelper, HandlerError}, @@ -46,7 +45,6 @@ pub async fn get_metadata( (StatusCode::OK, Json(response)) } -#[debug_handler] pub async fn get_tokens( State(state): State, Json(req): Json, @@ -70,26 +68,65 @@ pub async fn get_tokens( let target_account_id = AccountId::from_hex(req.account_id.as_str()) .map_err(|err| HandlerError::BadRequest(err.to_string()))?; - // Execute transaction - info!(target: COMPONENT, "Executing mint transaction for account."); - let (executed_tx, created_note) = client.execute_mint_transaction( - target_account_id, - req.is_private_note, - req.asset_amount, - )?; - - let mut updated_faucet_account = client.data_store().faucet_account(); - updated_faucet_account - .apply_delta(executed_tx.account_delta()) - .or_fail("Failed to apply faucet account delta")?; - - client.data_store().save_next_faucet_state(&updated_faucet_account).await?; - - // Run transaction prover & send transaction to node - info!(target: COMPONENT, "Proving and submitting transaction."); - let block_height = client.prove_and_submit_transaction(executed_tx).await?; + let mut faucet_account = client.data_store().faucet_account(); + let (created_note, block_height) = loop { + // Execute transaction + info!(target: COMPONENT, "Executing mint transaction for account."); + let (executed_tx, created_note) = client.execute_mint_transaction( + target_account_id, + req.is_private_note, + req.asset_amount, + )?; + + let prev_hash = faucet_account.hash(); + + faucet_account + .apply_delta(executed_tx.account_delta()) + .or_fail("Failed to apply faucet account delta")?; + + let new_hash = faucet_account.hash(); + + // Run transaction prover & send transaction to node + info!(target: COMPONENT, "Proving and submitting transaction."); + match client.prove_and_submit_transaction(executed_tx.clone()).await { + Ok(block_height) => { + break (created_note, block_height); + }, + Err(err) => { + error!( + target: COMPONENT, + %err, + "Failed to prove and submit transaction", + ); + + info!(target: COMPONENT, "Trying to request account state from the node..."); + let (got_faucet_account, block_num) = client.request_account_state().await?; + let got_new_hash = got_faucet_account.hash(); + info!( + target: COMPONENT, + %prev_hash, + %new_hash, + %got_new_hash, + "Received new account state from the node.", + ); + // If the hash hasn't changed, then the account's state we had is correct, + // and we should not try to execute the transaction again. We can just return error + // to the caller. + if new_hash == prev_hash { + return Err(err); + } + // If the new hash from the node is the same, as we expected to have, then + // transaction was successfully executed despite the error. Don't need to retry. + if new_hash == got_new_hash { + break (created_note, block_num); + } + + faucet_account = got_faucet_account; + }, + } + }; - client.data_store_mut().update_faucet_state(updated_faucet_account).await?; + client.data_store().update_faucet_state(faucet_account).await?; let note_id: NoteId = created_note.id(); let note_details = diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 5905a27d..265bc3d6 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -1,26 +1,17 @@ -use std::{ - path::{Path, PathBuf}, - sync::{Arc, RwLock}, -}; +use std::sync::{Arc, RwLock}; -use anyhow::{bail, Context}; use miden_objects::{ accounts::{Account, AccountId}, - crypto::hash::rpo::RpoDigest, notes::NoteId, transaction::{ChainMmr, InputNotes, TransactionInputs}, - utils::{Deserializable, Serializable}, BlockHeader, Word, }; use miden_tx::{DataStore, DataStoreError}; -use crate::errors::{ErrorHelper, HandlerError}; - -const FAUCET_ACCOUNT_FILENAME: &str = "faucet-account.bin"; +use crate::errors::HandlerError; #[derive(Clone)] pub struct FaucetDataStore { - storage_path: PathBuf, faucet_account: Arc>, seed: Word, block_header: BlockHeader, @@ -32,14 +23,12 @@ pub struct FaucetDataStore { impl FaucetDataStore { pub fn new( - storage_path: PathBuf, faucet_account: Arc>, seed: Word, root_block_header: BlockHeader, root_chain_mmr: ChainMmr, ) -> Self { Self { - storage_path, faucet_account, seed, block_header: root_block_header, @@ -52,36 +41,8 @@ impl FaucetDataStore { self.faucet_account.read().expect("Poisoned lock").clone() } - /// Saves the next faucet state to file. - pub async fn save_next_faucet_state( - &self, - next_faucet_state: &Account, - ) -> Result<(), HandlerError> { - tokio::fs::create_dir_all(&self.storage_path) - .await - .or_fail("Failed to create directory for storage")?; - - tokio::fs::write(next_faucet_state_path(&self.storage_path), next_faucet_state.to_bytes()) - .await - .or_fail("Failed to save faucet account to file") - } - - /// Switches file storage to the next faucet state. - pub async fn switch_to_next_faucet_state(&self) -> Result<(), HandlerError> { - tokio::fs::rename( - next_faucet_state_path(&self.storage_path), - faucet_state_path(&self.storage_path), - ) - .await - .or_fail("Failed to rename next faucet account file to the current one") - } - /// Updates the stored faucet account with the new one. - pub async fn update_faucet_state( - &mut self, - new_faucet_state: Account, - ) -> Result<(), HandlerError> { - self.switch_to_next_faucet_state().await?; + pub async fn update_faucet_state(&self, new_faucet_state: Account) -> Result<(), HandlerError> { *self.faucet_account.write().expect("Poisoned lock") = new_faucet_state; Ok(()) @@ -113,70 +74,3 @@ impl DataStore for FaucetDataStore { .map_err(DataStoreError::InvalidTransactionInput) } } - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Tries to restore the faucet state from current and/or next file(s). -/// -/// If the faucet state with the expected hash is not found in either file, an error is returned. -pub async fn resolve_faucet_state( - storage_path: impl AsRef, - expected_hash: RpoDigest, -) -> anyhow::Result { - if !faucet_state_path(&storage_path).exists() { - bail!("Faucet state file does not exist"); - } - - let current_state = load_current_faucet_state(&storage_path) - .await - .context("Failed to restore current faucet state")?; - - if current_state.hash() == expected_hash { - return Ok(current_state); - } - - if !next_faucet_state_path(&storage_path).exists() { - bail!("Next faucet state file does not exist"); - } - - let next_state = load_next_faucet_state(&storage_path) - .await - .context("Failed to restore next faucet state")?; - - if next_state.hash() == expected_hash { - return Ok(next_state); - } - - bail!( - "Failed to restore faucet state from files. Could not find file with expected state hash." - ); -} - -/// Loads the current faucet state from file. -pub async fn load_current_faucet_state(storage_path: impl AsRef) -> anyhow::Result { - load_faucet_state_internal(faucet_state_path(storage_path)).await -} - -/// Loads the next faucet state from file. -pub async fn load_next_faucet_state(storage_path: impl AsRef) -> anyhow::Result { - load_faucet_state_internal(next_faucet_state_path(storage_path)).await -} - -/// Loads the faucet state from file with the given path. -async fn load_faucet_state_internal(path: impl AsRef) -> anyhow::Result { - let bytes = tokio::fs::read(path).await.context("Failed to read faucet account from file")?; - - Account::read_from_bytes(&bytes) - .map_err(|err| anyhow::anyhow!("Failed to deserialize faucet account from bytes: {err}")) -} - -/// Returns path to the faucet state file. -fn faucet_state_path(storage_path: impl AsRef) -> PathBuf { - storage_path.as_ref().join(FAUCET_ACCOUNT_FILENAME) -} - -/// Returns path to the next faucet state file. -fn next_faucet_state_path(storage_path: impl AsRef) -> PathBuf { - faucet_state_path(storage_path).with_extension("next") -} From f984e673ce330ea26b60208fff9c9422e25eefa5 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 19:34:44 +0500 Subject: [PATCH 05/29] feat: introduce `ClientError` --- bin/faucet/src/client.rs | 50 ++++++++++++++++++-------------------- bin/faucet/src/errors.rs | 27 +++++++++++++------- bin/faucet/src/handlers.rs | 2 +- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 4be2cbba..36367b3a 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -40,7 +40,7 @@ use tonic::transport::Channel; use crate::{ config::FaucetConfig, - errors::{ErrorHelper, HandlerError}, + errors::{ClientError, ImplError}, store::FaucetDataStore, }; @@ -64,7 +64,7 @@ unsafe impl Send for FaucetClient {} impl FaucetClient { /// Creates a new faucet client. - pub async fn new(config: &FaucetConfig) -> anyhow::Result { + pub async fn new(config: &FaucetConfig) -> Result { let (rpc_api, root_block_header, root_chain_mmr) = initialize_faucet_client(config).await?; let init_seed: [u8; 32] = [0; 32]; let (auth_scheme, authenticator) = init_authenticator(init_seed, &config.secret_key_path) @@ -97,9 +97,9 @@ impl FaucetClient { target_account_id: AccountId, is_private_note: bool, asset_amount: u64, - ) -> Result<(ExecutedTransaction, Note), HandlerError> { + ) -> Result<(ExecutedTransaction, Note), ClientError> { let asset = - FungibleAsset::new(self.id, asset_amount).or_fail("Failed to create fungible asset")?; + FungibleAsset::new(self.id, asset_amount).context("Failed to create fungible asset")?; let note_type = if is_private_note { NoteType::Private @@ -115,14 +115,14 @@ impl FaucetClient { Default::default(), &mut self.rng, ) - .or_fail("Failed to create P2ID note")?; + .context("Failed to create P2ID note")?; let transaction_args = build_transaction_arguments(&output_note, note_type, asset)?; let executed_tx = self .executor .execute_transaction(self.id, 0, &[], transaction_args) - .or_fail("Failed to execute transaction")?; + .context("Failed to execute transaction")?; Ok((executed_tx, output_note)) } @@ -131,7 +131,7 @@ impl FaucetClient { pub async fn prove_and_submit_transaction( &mut self, executed_tx: ExecutedTransaction, - ) -> Result { + ) -> Result { // Prepare request with proven transaction. // This is needed to be in a separated code block in order to release reference to avoid // borrow checker error. @@ -139,7 +139,7 @@ impl FaucetClient { let transaction_prover = LocalTransactionProver::new(ProvingOptions::default()); let proven_transaction = - transaction_prover.prove(executed_tx).or_fail("Failed to prove transaction")?; + transaction_prover.prove(executed_tx).context("Failed to prove transaction")?; SubmitProvenTransactionRequest { transaction: proven_transaction.to_bytes(), @@ -150,7 +150,7 @@ impl FaucetClient { .rpc_api .submit_proven_transaction(request) .await - .or_fail("Failed to submit proven transaction")?; + .context("Failed to submit proven transaction")?; Ok(response.into_inner().block_height) } @@ -158,25 +158,22 @@ impl FaucetClient { /// Requests faucet account state from the node. /// /// The account is expected to be public, otherwise, the error is returned. - pub async fn request_account_state(&mut self) -> Result<(Account, u32), HandlerError> { + pub async fn request_account_state(&mut self) -> Result<(Account, u32), ClientError> { let account_info = self .rpc_api .get_account_details(GetAccountDetailsRequest { account_id: Some(self.id.into()) }) .await - .or_fail("Failed to get faucet account state")? + .context("Failed to get faucet account state")? .into_inner() .details - .or_fail("Account info field is empty")?; + .context("Account info field is empty")?; let faucet_account_state_bytes = - account_info.details.or_fail("Account details field is empty")?; - let faucet_account = - Account::read_from_bytes(&faucet_account_state_bytes).map_err(|err| { - HandlerError::InternalServerError(format!( - "Failed to deserialize faucet account: {err}" - )) - })?; - let block_num = account_info.summary.or_fail("Account summary field is empty")?.block_num; + account_info.details.context("Account details field is empty")?; + let faucet_account = Account::read_from_bytes(&faucet_account_state_bytes) + .map_err(ImplError) + .context("Failed to deserialize faucet account")?; + let block_num = account_info.summary.context("Account summary field is empty")?.block_num; Ok((faucet_account, block_num)) } @@ -202,13 +199,14 @@ impl FaucetClient { fn init_authenticator( init_seed: [u8; 32], secret_key_path: impl AsRef, -) -> anyhow::Result<(AuthScheme, BasicAuthenticator)> { +) -> Result<(AuthScheme, BasicAuthenticator), ClientError> { // Load secret key from file or generate new one let secret = if secret_key_path.as_ref().exists() { SecretKey::read_from_bytes( &std::fs::read(secret_key_path).context("Failed to read secret key from file")?, ) - .map_err(|err| anyhow!("Failed to deserialize secret key: {err}"))? + .map_err(ImplError) + .context("Failed to deserialize secret key")? } else { let mut rng = ChaCha20Rng::from_seed(init_seed); let secret = SecretKey::with_rng(&mut rng); @@ -235,7 +233,7 @@ fn build_account( config: &FaucetConfig, init_seed: [u8; 32], auth_scheme: AuthScheme, -) -> anyhow::Result<(Account, Word)> { +) -> Result<(Account, Word), ClientError> { let token_symbol = TokenSymbol::new(config.token_symbol.as_str()) .context("Failed to parse token symbol from configuration file")?; @@ -256,7 +254,7 @@ fn build_account( /// Initializes the faucet client by connecting to the node and fetching the root block header. pub async fn initialize_faucet_client( config: &FaucetConfig, -) -> anyhow::Result<(ApiClient, BlockHeader, ChainMmr)> { +) -> Result<(ApiClient, BlockHeader, ChainMmr), ClientError> { let endpoint = tonic::transport::Endpoint::try_from(config.node_url.clone()) .context("Failed to parse node URL from configuration file")? .timeout(Duration::from_millis(config.timeout_ms)); @@ -295,7 +293,7 @@ fn build_transaction_arguments( output_note: &Note, note_type: NoteType, asset: FungibleAsset, -) -> Result { +) -> Result { let recipient = output_note .recipient() .digest() @@ -317,7 +315,7 @@ fn build_transaction_arguments( .replace("{execution_hint}", &Felt::new(execution_hint).to_string()); let script = TransactionScript::compile(script, vec![], TransactionKernel::assembler()) - .or_fail("Failed to compile script")?; + .context("Failed to compile script")?; let mut transaction_args = TransactionArgs::new(Some(script), None, AdviceMap::new()); transaction_args.extend_expected_output_notes(vec![output_note.clone()]); diff --git a/bin/faucet/src/errors.rs b/bin/faucet/src/errors.rs index 029094e9..5a7e4084 100644 --- a/bin/faucet/src/errors.rs +++ b/bin/faucet/src/errors.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt::{Debug, Display}; use axum::{ http::{header, StatusCode}, @@ -6,8 +6,21 @@ use axum::{ }; use thiserror::Error; +/// Wrapper for implementing `Error` trait for errors, which do not implement it, like +/// [miden_objects::crypto::utils::DeserializationError] and other error types from `miden-base`. +#[derive(Debug, Error)] +#[error("{0}")] +pub struct ImplError(pub E); + +#[derive(Debug, Error)] +#[error("Client error: {0:#}")] +pub struct ClientError(#[from] anyhow::Error); + #[derive(Debug, Error)] pub enum HandlerError { + #[error("Node client error: {0}")] + ClientError(#[from] ClientError), + #[error("Client has submitted a bad request: {0}")] BadRequest(String), @@ -23,14 +36,16 @@ impl HandlerError { match *self { Self::BadRequest(_) => StatusCode::BAD_REQUEST, Self::NotFound(_) => StatusCode::NOT_FOUND, - Self::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::ClientError(_) | Self::InternalServerError(_) => { + StatusCode::INTERNAL_SERVER_ERROR + }, } } fn message(&self) -> String { match self { Self::BadRequest(msg) => msg, - Self::InternalServerError(_) => "Error processing request", + Self::ClientError(_) | Self::InternalServerError(_) => "Error processing request", Self::NotFound(msg) => msg, } .to_string() @@ -57,9 +72,3 @@ impl ErrorHelper for Result { self.map_err(|err| HandlerError::InternalServerError(format!("{message}: {err}"))) } } - -impl ErrorHelper for Option { - fn or_fail(self, message: impl Display) -> Result { - self.ok_or_else(|| HandlerError::InternalServerError(message.to_string())) - } -} diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index 8bd31597..26cc1b86 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -113,7 +113,7 @@ pub async fn get_tokens( // and we should not try to execute the transaction again. We can just return error // to the caller. if new_hash == prev_hash { - return Err(err); + return Err(err.into()); } // If the new hash from the node is the same, as we expected to have, then // transaction was successfully executed despite the error. Don't need to retry. From 8e12b045361fc3095b3fcf50aee0db6dfca99632 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:08:45 +0500 Subject: [PATCH 06/29] fix: update config --- config/miden-faucet.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/miden-faucet.toml b/config/miden-faucet.toml index a9fdc142..688e2ac4 100644 --- a/config/miden-faucet.toml +++ b/config/miden-faucet.toml @@ -1,6 +1,7 @@ endpoint = { host = "localhost", port = 8080 } node_url = "http://localhost:57291" timeout_ms = 10000 +secret_key_path = "faucet-secret.key" # Data used to construct the faucet account of the faucet asset_amount_options = [100, 500, 1000] From 4fc851f859d1b12690f05880613f928b187179a2 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:09:22 +0500 Subject: [PATCH 07/29] fix: save new faucet state to in-memory store after getting from the server --- bin/faucet/src/handlers.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index 26cc1b86..c83ea731 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -68,8 +68,9 @@ pub async fn get_tokens( let target_account_id = AccountId::from_hex(req.account_id.as_str()) .map_err(|err| HandlerError::BadRequest(err.to_string()))?; - let mut faucet_account = client.data_store().faucet_account(); let (created_note, block_height) = loop { + let mut faucet_account = client.data_store().faucet_account(); + // Execute transaction info!(target: COMPONENT, "Executing mint transaction for account."); let (executed_tx, created_note) = client.execute_mint_transaction( @@ -107,7 +108,7 @@ pub async fn get_tokens( %prev_hash, %new_hash, %got_new_hash, - "Received new account state from the node.", + "Received new account state from the node", ); // If the hash hasn't changed, then the account's state we had is correct, // and we should not try to execute the transaction again. We can just return error @@ -121,13 +122,11 @@ pub async fn get_tokens( break (created_note, block_num); } - faucet_account = got_faucet_account; + client.data_store().update_faucet_state(got_faucet_account).await?; }, } }; - client.data_store().update_faucet_state(faucet_account).await?; - let note_id: NoteId = created_note.id(); let note_details = NoteDetails::new(created_note.assets().clone(), created_note.recipient().clone()); From cebc3729ec5731912f43792059b048360cab7f9d Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:11:28 +0500 Subject: [PATCH 08/29] fix: remove `macros` feature from axum dependency --- Cargo.lock | 12 ------------ bin/faucet/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9958a561..5a290bb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,7 +191,6 @@ checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", - "axum-macros", "bytes", "futures-util", "http", @@ -239,17 +238,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "backtrace" version = "0.3.74" diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index b4fa4715..2d047d95 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -18,7 +18,7 @@ testing = ["miden-objects/testing", "miden-lib/testing"] [dependencies] anyhow = "1.0" -axum = { version = "0.7", features = ["tokio", "macros"] } +axum = { version = "0.7", features = ["tokio"] } clap = { version = "4.5", features = ["derive", "string"] } figment = { version = "0.10", features = ["toml", "env"] } http = "1.1" From be90b6bd1963fd9c015d7d2a6cb60b83859318e4 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:46:02 +0500 Subject: [PATCH 09/29] docs: update `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88d847b0..3733d508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Migrated faucet from actix-web to axum (#511). - Changed the `BlockWitness` to pass the inputs to the VM using only advice provider (#516). - [BREAKING] Improved store API errors (return "not found" instead of "internal error" status if requested account(s) not found) (#518). +- Support for faucet restarting without blockchain restarting (#517). ## 0.5.1 (2024-09-12) From b808a80121fb7c8c17dbbc202fa5febbde1e70b8 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:49:04 +0500 Subject: [PATCH 10/29] docs: add comment for `seed` field Co-authored-by: Mirko <48352201+Mirko-von-Leipzig@users.noreply.github.com> --- bin/faucet/src/store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 265bc3d6..8bd38b99 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -13,6 +13,7 @@ use crate::errors::HandlerError; #[derive(Clone)] pub struct FaucetDataStore { faucet_account: Arc>, + /// Seed used for faucet account creation. seed: Word, block_header: BlockHeader, chain_mmr: ChainMmr, From dafc01b395d824925e186947e607228c359c74b9 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 21 Oct 2024 19:18:44 +0500 Subject: [PATCH 11/29] fix: log message Co-authored-by: Mirko <48352201+Mirko-von-Leipzig@users.noreply.github.com> --- bin/faucet/src/handlers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index c83ea731..3018f185 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -100,7 +100,7 @@ pub async fn get_tokens( "Failed to prove and submit transaction", ); - info!(target: COMPONENT, "Trying to request account state from the node..."); + info!(target: COMPONENT, "Requesting account state from the node..."); let (got_faucet_account, block_num) = client.request_account_state().await?; let got_new_hash = got_faucet_account.hash(); info!( From 50fd73ebda2f4160b2c30a8602be0305dd3e831f Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Tue, 22 Oct 2024 00:18:30 +0500 Subject: [PATCH 12/29] refactor: address review comments --- bin/faucet/src/errors.rs | 22 +++++----------------- bin/faucet/src/handlers.rs | 20 +++++++++++--------- bin/faucet/src/store.rs | 3 +-- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/bin/faucet/src/errors.rs b/bin/faucet/src/errors.rs index 5a7e4084..e2d393a0 100644 --- a/bin/faucet/src/errors.rs +++ b/bin/faucet/src/errors.rs @@ -21,12 +21,12 @@ pub enum HandlerError { #[error("Node client error: {0}")] ClientError(#[from] ClientError), + #[error("Server has encountered an internal error: {0:#}")] + Internal(#[from] anyhow::Error), + #[error("Client has submitted a bad request: {0}")] BadRequest(String), - #[error("Server has encountered an internal error: {0}")] - InternalServerError(String), - #[error("Page not found: {0}")] NotFound(String), } @@ -36,16 +36,14 @@ impl HandlerError { match *self { Self::BadRequest(_) => StatusCode::BAD_REQUEST, Self::NotFound(_) => StatusCode::NOT_FOUND, - Self::ClientError(_) | Self::InternalServerError(_) => { - StatusCode::INTERNAL_SERVER_ERROR - }, + Self::ClientError(_) | Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR, } } fn message(&self) -> String { match self { Self::BadRequest(msg) => msg, - Self::ClientError(_) | Self::InternalServerError(_) => "Error processing request", + Self::ClientError(_) | Self::Internal(_) => "Error processing request", Self::NotFound(msg) => msg, } .to_string() @@ -62,13 +60,3 @@ impl IntoResponse for HandlerError { .into_response() } } - -pub trait ErrorHelper { - fn or_fail(self, message: impl Display) -> Result; -} - -impl ErrorHelper for Result { - fn or_fail(self, message: impl Display) -> Result { - self.map_err(|err| HandlerError::InternalServerError(format!("{message}: {err}"))) - } -} diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index 3018f185..5b4ddd97 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use axum::{ extract::{Path, State}, http::{Response, StatusCode}, @@ -15,11 +16,7 @@ use serde::{Deserialize, Serialize}; use tonic::body; use tracing::{error, info}; -use crate::{ - errors::{ErrorHelper, HandlerError}, - state::FaucetState, - COMPONENT, -}; +use crate::{errors::HandlerError, state::FaucetState, COMPONENT}; #[derive(Deserialize)] pub struct FaucetRequest { @@ -83,7 +80,7 @@ pub async fn get_tokens( faucet_account .apply_delta(executed_tx.account_delta()) - .or_fail("Failed to apply faucet account delta")?; + .context("Failed to apply faucet account delta")?; let new_hash = faucet_account.hash(); @@ -100,6 +97,9 @@ pub async fn get_tokens( "Failed to prove and submit transaction", ); + // TODO: Improve error statuses returned from the `SubmitProvenTransaction` endpoint + // of block producer. Check received error status here. + info!(target: COMPONENT, "Requesting account state from the node..."); let (got_faucet_account, block_num) = client.request_account_state().await?; let got_new_hash = got_faucet_account.hash(); @@ -132,7 +132,7 @@ pub async fn get_tokens( NoteDetails::new(created_note.assets().clone(), created_note.recipient().clone()); let note_tag = NoteTag::from_account_id(target_account_id, NoteExecutionMode::Local) - .or_fail("failed to build note tag for local execution")?; + .context("failed to build note tag for local execution")?; // Serialize note into bytes let bytes = NoteFile::NoteDetails { @@ -151,7 +151,8 @@ pub async fn get_tokens( .header(header::CONTENT_DISPOSITION, "attachment; filename=note.mno") .header("Note-Id", note_id.to_string()) .body(body::boxed(Full::from(bytes))) - .or_fail("Failed to build response") + .context("Failed to build response") + .map_err(Into::into) } pub async fn get_index(state: State) -> Result { @@ -170,5 +171,6 @@ pub async fn get_static_file( .status(StatusCode::OK) .header(header::CONTENT_TYPE, static_file.mime_type) .body(body::boxed(Full::from(static_file.data))) - .or_fail("Failed to build response") + .context("Failed to build response") + .map_err(Into::into) } diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 8bd38b99..8b1b15ec 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -62,8 +62,7 @@ impl DataStore for FaucetDataStore { return Err(DataStoreError::AccountNotFound(account_id)); } - let empty_input_notes = - InputNotes::new(Vec::new()).map_err(DataStoreError::InvalidTransactionInput)?; + let empty_input_notes = InputNotes::new(vec![]).expect("Empty notes must succeed"); TransactionInputs::new( account.clone(), From 8db4ae9b29eb48accf1cc80f9a533fdd75fde7ee Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 25 Oct 2024 00:35:50 +0300 Subject: [PATCH 13/29] refactor: initial changes --- Cargo.lock | 289 +++++++++--------- Cargo.toml | 14 +- bin/faucet/src/client.rs | 14 +- .../prover/asm/block_kernel.masm | 4 + .../src/block_builder/prover/mod.rs | 2 +- .../src/block_builder/prover/tests.rs | 2 +- crates/block-producer/src/test_utils/block.rs | 4 +- crates/block-producer/src/test_utils/store.rs | 10 +- crates/store/src/state.rs | 29 +- rust-toolchain.toml | 2 +- 10 files changed, 187 insertions(+), 183 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e110537a..c3ff8db1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "arrayref" @@ -131,9 +131,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -276,9 +276,9 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", @@ -345,9 +345,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -357,9 +357,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "camino" @@ -395,9 +395,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -456,9 +456,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -466,9 +466,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -648,18 +648,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn", @@ -823,9 +823,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -837,9 +837,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -847,33 +847,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-sink", @@ -918,9 +918,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -940,7 +940,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -962,6 +962,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.9.1" @@ -1025,9 +1031,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1037,9 +1043,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -1135,12 +1141,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -1205,9 +1211,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -1234,7 +1240,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "string_cache", "term", "tiny-keccak", @@ -1262,9 +1268,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -1346,7 +1352,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "syn", ] @@ -1396,8 +1402,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miden-air" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2702f8adb96844e3521f49149f6c3d4773ecdd2a96a3169e3c025a2e3ee32b5e" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "miden-core", "miden-thiserror", @@ -1408,8 +1413,7 @@ dependencies = [ [[package]] name = "miden-assembly" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae9cef4fbafb4fe26da18574bcdbd78815857cfe1099760782701ababb076c2" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "aho-corasick", "lalrpop", @@ -1426,8 +1430,7 @@ dependencies = [ [[package]] name = "miden-core" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3f6db878d6b56c1566cd5b832908675566d3919b9a3523d630dfb5e2f7422d" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "lock_api", "loom", @@ -1445,9 +1448,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825fc5d2e4c951f45da54da2d0d23071918d966133f85dbc219694e0b9a1e141" +checksum = "1e0ca714c8242f329b9ea6f1a5bf0e93f1490f348f982e3a606d91b884254308" dependencies = [ "blake3", "cc", @@ -1502,7 +1505,7 @@ dependencies = [ [[package]] name = "miden-lib" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#9dc06d705b8f4cae86ff08a293f379c206f13421" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" dependencies = [ "miden-assembly", "miden-objects", @@ -1696,7 +1699,7 @@ dependencies = [ [[package]] name = "miden-objects" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#9dc06d705b8f4cae86ff08a293f379c206f13421" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" dependencies = [ "miden-assembly", "miden-core", @@ -1710,8 +1713,7 @@ dependencies = [ [[package]] name = "miden-processor" version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04a128e20400086c9a985f4e5702e438ba781338fb0bdf9acff16d996c640087" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "miden-air", "miden-core", @@ -1722,8 +1724,7 @@ dependencies = [ [[package]] name = "miden-prover" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680d9203cee09b3cee6f79dc952a15f18a1eb47744ffac4f4e559d02bfcdee7a" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "miden-air", "miden-processor", @@ -1738,8 +1739,7 @@ version = "0.6.0" [[package]] name = "miden-stdlib" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41623ad4f4ea6449760f70ab8928c682c3824d735d3e330f07e3d24d1ad20bfa" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "miden-assembly", ] @@ -1767,8 +1767,10 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#9dc06d705b8f4cae86ff08a293f379c206f13421" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" dependencies = [ + "async-trait", + "miden-assembly", "miden-lib", "miden-objects", "miden-processor", @@ -1776,14 +1778,15 @@ dependencies = [ "miden-verifier", "rand", "rand_chacha", - "winter-maybe-async 0.10.0", + "regex", + "walkdir", + "winter-maybe-async 0.10.1", ] [[package]] name = "miden-verifier" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8144a126ecb1941122e8261e85f2e8e94c0b82740bc23879c0a14263f48797" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" dependencies = [ "miden-air", "miden-core", @@ -2009,18 +2012,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "option-ext" @@ -2114,7 +2117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -2128,18 +2131,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -2148,9 +2151,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2187,9 +2190,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", "syn", @@ -2197,9 +2200,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -2231,7 +2234,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -2406,9 +2409,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] @@ -2426,14 +2429,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2447,13 +2450,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2464,9 +2467,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rusqlite" @@ -2537,9 +2540,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -2606,18 +2609,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ "proc-macro2", "quote", @@ -2626,9 +2629,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -2797,9 +2800,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -2818,11 +2821,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -2874,18 +2883,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", @@ -2946,9 +2955,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -3022,7 +3031,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -3269,15 +3278,16 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.99" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" dependencies = [ "dissimilar", "glob", "serde", "serde_derive", "serde_json", + "target-triple", "termcolor", "toml", ] @@ -3305,12 +3315,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" @@ -3456,9 +3463,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -3467,9 +3474,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -3482,9 +3489,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3492,9 +3499,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -3505,9 +3512,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "winapi" @@ -3828,9 +3835,9 @@ dependencies = [ [[package]] name = "winter-maybe-async" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77eafc7944188b941c90759b938b8377247ee6e440952a0a7c4dbde7edc4ecbb" +checksum = "be43529f43f70306437d2c2c9f9e2b3a4d39b42e86702d8d7577f2357ea32fa6" dependencies = [ "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 656515f3..25fa1ee3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ resolver = "2" [workspace.package] edition = "2021" -rust-version = "1.80" +rust-version = "1.82" version = "0.6.0" license = "MIT" authors = ["Miden contributors"] @@ -25,8 +25,8 @@ exclude = [".github/"] readme = "README.md" [workspace.dependencies] -miden-air = { version = "0.10", default-features = false } -miden-lib = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "next"} +miden-air = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule" } +miden-lib = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } miden-node-block-producer = { path = "crates/block-producer", version = "0.6" } miden-node-faucet = { path = "bin/faucet", version = "0.6" } miden-node-proto = { path = "crates/proto", version = "0.6" } @@ -35,10 +35,10 @@ miden-node-rpc-proto = { path = "crates/rpc-proto", version = "0.6" } miden-node-store = { path = "crates/store", version = "0.6" } miden-node-test-macro = { path = "crates/test-macro" } miden-node-utils = { path = "crates/utils", version = "0.6" } -miden-objects = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "next" } -miden-processor = { version = "0.10" } -miden-stdlib = { version = "0.10", default-features = false } -miden-tx = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "next" } +miden-objects = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } +miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule" } +miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule", default-features = false } +miden-tx = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } prost = { version = "0.13" } thiserror = { version = "1.0" } tokio = { version = "1.40", features = ["rt-multi-thread"] } diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index d02f4a13..e6a4f51d 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{cell::RefCell, rc::Rc, sync::Arc, time::Duration}; use miden_lib::{ accounts::faucets::create_basic_fungible_faucet, notes::create_p2id_note, @@ -45,7 +45,7 @@ pub const DISTRIBUTE_FUNGIBLE_ASSET_SCRIPT: &str = /// for the faucet. pub struct FaucetClient { rpc_api: ApiClient, - executor: TransactionExecutor>, + executor: TransactionExecutor, data_store: FaucetDataStore, id: AccountId, rng: RpoRandomCoin, @@ -73,7 +73,8 @@ impl FaucetClient { secret.public_key().into(), AuthSecretKey::RpoFalcon512(secret), )]); - let executor = TransactionExecutor::new(data_store.clone(), Some(Rc::new(authenticator))); + let executor = + TransactionExecutor::new(Arc::new(data_store.clone()), Some(Arc::new(authenticator))); let mut rng = thread_rng(); let coin_seed: [u64; 4] = rng.gen(); @@ -135,9 +136,10 @@ impl FaucetClient { let request = { let transaction_prover = LocalTransactionProver::new(ProvingOptions::default()); - let proven_transaction = transaction_prover.prove(executed_tx).map_err(|err| { - ProcessError::InternalServerError(format!("Failed to prove transaction: {err}")) - })?; + let proven_transaction = + transaction_prover.prove(executed_tx.into()).map_err(|err| { + ProcessError::InternalServerError(format!("Failed to prove transaction: {err}")) + })?; SubmitProvenTransactionRequest { transaction: proven_transaction.to_bytes(), diff --git a/crates/block-producer/src/block_builder/prover/asm/block_kernel.masm b/crates/block-producer/src/block_builder/prover/asm/block_kernel.masm index 48139646..63e27704 100644 --- a/crates/block-producer/src/block_builder/prover/asm/block_kernel.masm +++ b/crates/block-producer/src/block_builder/prover/asm/block_kernel.masm @@ -6,6 +6,7 @@ use.std::collections::smt use.std::collections::mmr +use.std::sys const.ACCOUNT_TREE_DEPTH=64 const.BLOCK_NOTES_BATCH_TREE_DEPTH=6 @@ -237,4 +238,7 @@ begin # Load output on stack padw mem_loadw.2 padw mem_loadw.1 padw mem_loadw.0 # => [ACCOUNT_ROOT, NOTE_ROOT, NULLIFIER_ROOT, CHAIN_MMR_ROOT] + + # truncate the stack + exec.sys::truncate_stack end diff --git a/crates/block-producer/src/block_builder/prover/mod.rs b/crates/block-producer/src/block_builder/prover/mod.rs index 17f7d642..cede229e 100644 --- a/crates/block-producer/src/block_builder/prover/mod.rs +++ b/crates/block-producer/src/block_builder/prover/mod.rs @@ -88,7 +88,7 @@ impl BlockProver { let advice_provider = MemAdviceProvider::from(advice_inputs); let mut host = DefaultHost::new(advice_provider); - host.load_mast_forest(StdLibrary::default().into()); + host.load_mast_forest(StdLibrary::default().mast_forest().clone()); host }; diff --git a/crates/block-producer/src/block_builder/prover/tests.rs b/crates/block-producer/src/block_builder/prover/tests.rs index 09f98d78..3d776ffc 100644 --- a/crates/block-producer/src/block_builder/prover/tests.rs +++ b/crates/block-producer/src/block_builder/prover/tests.rs @@ -843,7 +843,7 @@ async fn test_compute_chain_mmr_root_mmr_17_peaks() { mmr.add(Digest::default()); } - assert_eq!(mmr.peaks(mmr.forest()).unwrap().peaks().len(), 17); + assert_eq!(mmr.peaks().peaks().len(), 17); mmr }; diff --git a/crates/block-producer/src/test_utils/block.rs b/crates/block-producer/src/test_utils/block.rs index a8678eea..71d15f7e 100644 --- a/crates/block-producer/src/test_utils/block.rs +++ b/crates/block-producer/src/test_utils/block.rs @@ -49,7 +49,7 @@ pub async fn build_expected_block_header( store_chain_mmr.add(last_block_header.hash()); - store_chain_mmr.peaks(store_chain_mmr.forest()).unwrap().hash_peaks() + store_chain_mmr.peaks().hash_peaks() }; let note_created_smt = note_created_smt_from_note_batches(block_output_notes(batches.iter())); @@ -157,7 +157,7 @@ impl MockBlockBuilder { 0, self.last_block_header.hash(), self.last_block_header.block_num() + 1, - self.store_chain_mmr.peaks(self.store_chain_mmr.forest()).unwrap().hash_peaks(), + self.store_chain_mmr.peaks().hash_peaks(), self.store_accounts.root(), Digest::default(), note_created_smt_from_note_batches(created_notes.iter()).root(), diff --git a/crates/block-producer/src/test_utils/store.rs b/crates/block-producer/src/test_utils/store.rs index bf1e8932..f1fb7fef 100644 --- a/crates/block-producer/src/test_utils/store.rs +++ b/crates/block-producer/src/test_utils/store.rs @@ -121,7 +121,7 @@ impl MockStoreSuccessBuilder { 0, Digest::default(), block_num, - chain_mmr.peaks(chain_mmr.forest()).unwrap().hash_peaks(), + chain_mmr.peaks().hash_peaks(), accounts_smt.root(), nullifiers_smt.root(), note_root, @@ -301,7 +301,7 @@ impl Store for MockStoreSuccess { let chain_peaks = { let locked_chain_mmr = self.chain_mmr.read().await; - locked_chain_mmr.peaks(locked_chain_mmr.forest()).unwrap() + locked_chain_mmr.peaks() }; let accounts = { @@ -329,15 +329,13 @@ impl Store for MockStoreSuccess { *locked_headers.iter().max_by_key(|(block_num, _)| *block_num).unwrap().1; let locked_chain_mmr = self.chain_mmr.read().await; - let mmr_forest = locked_chain_mmr.forest(); let chain_length = latest_header.block_num(); let block_proofs = note_proofs .values() .map(|note_proof| { let block_num = note_proof.location().block_num(); let block_header = *locked_headers.get(&block_num).unwrap(); - let mmr_path = - locked_chain_mmr.open(block_num as usize, mmr_forest).unwrap().merkle_path; + let mmr_path = locked_chain_mmr.open(block_num as usize).unwrap().merkle_path; BlockInclusionProof { block_header, mmr_path, chain_length } }) @@ -376,7 +374,7 @@ impl Store for MockStoreSuccess { let block_num = note_proof.location().block_num(); let block_header = *locked_headers.get(&block_num).unwrap(); let mmr_path = locked_chain_mmr - .open(block_num as usize, latest_header.block_num() as usize) + .open_at(block_num as usize, latest_header.block_num() as usize) .unwrap() .merkle_path; diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 31b8607c..16968477 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -232,12 +232,7 @@ impl State { let mut chain_mmr = inner.chain_mmr.clone(); // new_block.chain_root must be equal to the chain MMR root prior to the update - let peaks = chain_mmr.peaks(chain_mmr.forest()).map_err(|error| { - ApplyBlockError::FailedToGetMmrPeaksForForest { - forest: chain_mmr.forest(), - error, - } - })?; + let peaks = chain_mmr.peaks(); if peaks.hash_peaks() != header.chain_root() { return Err(ApplyBlockError::NewBlockInvalidChainRoot); } @@ -372,8 +367,7 @@ impl State { if let Some(header) = block_header { let mmr_proof = if include_mmr_proof { let inner = self.inner.read().await; - let mmr_proof = - inner.chain_mmr.open(header.block_num() as usize, inner.chain_mmr.forest())?; + let mmr_proof = inner.chain_mmr.open(header.block_num() as usize)?; Some(mmr_proof) } else { None @@ -444,7 +438,7 @@ impl State { let paths = blocks .iter() .map(|&block_num| { - let proof = state.chain_mmr.open(block_num as usize, chain_length)?.merkle_path; + let proof = state.chain_mmr.open(block_num as usize)?.merkle_path; Ok::<_, MmrError>((block_num, proof)) }) @@ -548,9 +542,7 @@ impl State { let note_sync = self.db.get_note_sync(block_num, note_tags).await?; - let mmr_proof = inner - .chain_mmr - .open(note_sync.block_header.block_num() as usize, inner.chain_mmr.forest())?; + let mmr_proof = inner.chain_mmr.open(note_sync.block_header.block_num() as usize)?; Ok((note_sync, mmr_proof)) } @@ -580,12 +572,13 @@ impl State { // using current block number gets us the peaks of the chain MMR as of one block ago; // this is done so that latest.chain_root matches the returned peaks - let chain_peaks = inner.chain_mmr.peaks(latest.block_num() as usize).map_err(|error| { - GetBlockInputsError::FailedToGetMmrPeaksForForest { - forest: latest.block_num() as usize, - error, - } - })?; + let chain_peaks = + inner.chain_mmr.peaks_at(latest.block_num() as usize).map_err(|error| { + GetBlockInputsError::FailedToGetMmrPeaksForForest { + forest: latest.block_num() as usize, + error, + } + })?; let account_states = account_ids .iter() .cloned() diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b12899f1..6ad542bb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.80" +channel = "1.82" components = ["rustfmt", "rust-src", "clippy"] profile = "minimal" From a78828f8ccb1eaa0d42af2d81b77d6626794c197 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 25 Oct 2024 01:50:42 +0300 Subject: [PATCH 14/29] refactor: impl Send and Sync for FaucetDataStore --- Cargo.lock | 49 ++++++++++++++++++++-------------------- Cargo.toml | 6 ++--- bin/faucet/Cargo.toml | 1 + bin/faucet/src/client.rs | 19 +++++++++++----- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3ff8db1..4c5462a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -70,36 +70,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -496,9 +496,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "constant_time_eq" @@ -1402,7 +1402,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miden-air" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "miden-core", "miden-thiserror", @@ -1413,7 +1413,7 @@ dependencies = [ [[package]] name = "miden-assembly" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "aho-corasick", "lalrpop", @@ -1430,7 +1430,7 @@ dependencies = [ [[package]] name = "miden-core" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "lock_api", "loom", @@ -1491,6 +1491,7 @@ dependencies = [ "tower 0.5.1", "tower-http 0.6.1", "tracing", + "winter-maybe-async 0.10.1", ] [[package]] @@ -1505,7 +1506,7 @@ dependencies = [ [[package]] name = "miden-lib" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#d497bf53e3609c2f3d1cc595e4964fda33d5c933" dependencies = [ "miden-assembly", "miden-objects", @@ -1699,7 +1700,7 @@ dependencies = [ [[package]] name = "miden-objects" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#d497bf53e3609c2f3d1cc595e4964fda33d5c933" dependencies = [ "miden-assembly", "miden-core", @@ -1713,7 +1714,7 @@ dependencies = [ [[package]] name = "miden-processor" version = "0.10.6" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "miden-air", "miden-core", @@ -1724,7 +1725,7 @@ dependencies = [ [[package]] name = "miden-prover" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "miden-air", "miden-processor", @@ -1739,7 +1740,7 @@ version = "0.6.0" [[package]] name = "miden-stdlib" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "miden-assembly", ] @@ -1767,7 +1768,7 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#ebd7e17a68e5887107362c6c42cbe908fe492bfa" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=andrew-migrate-to-new-padding-rule#d497bf53e3609c2f3d1cc595e4964fda33d5c933" dependencies = [ "async-trait", "miden-assembly", @@ -1786,7 +1787,7 @@ dependencies = [ [[package]] name = "miden-verifier" version = "0.10.5" -source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=al-migrate-new-padding-rule#0b10225edc6eb0b5b7f8e9ff729e0c64b5c3a499" +source = "git+https://github.com/0xPolygonMiden/miden-vm.git?branch=next#ae34c56f53ae3194af4368da3e67370a116f9cd7" dependencies = [ "miden-air", "miden-core", diff --git a/Cargo.toml b/Cargo.toml index 25fa1ee3..43b2ed88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [".github/"] readme = "README.md" [workspace.dependencies] -miden-air = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule" } +miden-air = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next" } miden-lib = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } miden-node-block-producer = { path = "crates/block-producer", version = "0.6" } miden-node-faucet = { path = "bin/faucet", version = "0.6" } @@ -36,8 +36,8 @@ miden-node-store = { path = "crates/store", version = "0.6" } miden-node-test-macro = { path = "crates/test-macro" } miden-node-utils = { path = "crates/utils", version = "0.6" } miden-objects = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } -miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule" } -miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "al-migrate-new-padding-rule", default-features = false } +miden-processor = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next" } +miden-stdlib = { git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } miden-tx = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "andrew-migrate-to-new-padding-rule" } prost = { version = "0.13" } thiserror = { version = "1.0" } diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index fc03a81a..4b9b865c 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -39,6 +39,7 @@ tonic = { workspace = true } tower = "0.5" tower-http = { version = "0.6", features = ["cors", "set-header", "trace"] } tracing = { workspace = true } +winter-maybe-async = { version = "0.10" } [build-dependencies] # Required to inject build metadata. diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index e6a4f51d..a6d36c2b 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -22,13 +22,15 @@ use miden_objects::{ BlockHeader, Felt, Word, }; use miden_tx::{ - auth::BasicAuthenticator, utils::Serializable, DataStore, DataStoreError, - LocalTransactionProver, ProvingOptions, TransactionExecutor, TransactionInputs, - TransactionProver, + auth::{BasicAuthenticator, TransactionAuthenticator}, + utils::Serializable, + DataStore, DataStoreError, LocalTransactionProver, ProvingOptions, TransactionExecutor, + TransactionInputs, TransactionProver, }; use rand::{rngs::StdRng, thread_rng, Rng}; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; use tonic::transport::Channel; +use winter_maybe_async::*; use crate::{ config::FaucetConfig, @@ -69,12 +71,12 @@ impl FaucetClient { root_block_header, root_chain_mmr, ); - let authenticator = BasicAuthenticator::::new(&[( + let authenticator = Arc::new(BasicAuthenticator::::new(&[( secret.public_key().into(), AuthSecretKey::RpoFalcon512(secret), - )]); + )])) as Arc; let executor = - TransactionExecutor::new(Arc::new(data_store.clone()), Some(Arc::new(authenticator))); + TransactionExecutor::new(Arc::new(data_store.clone()), Some(authenticator.clone())); let mut rng = thread_rng(); let coin_seed: [u64; 4] = rng.gen(); @@ -199,7 +201,12 @@ impl FaucetDataStore { } } +unsafe impl Send for FaucetDataStore {} +unsafe impl Sync for FaucetDataStore {} + +#[maybe_async_trait] impl DataStore for FaucetDataStore { + #[maybe_async] fn get_transaction_inputs( &self, account_id: AccountId, From af6a0f38dedb44532554ab3b450145ae8bdfcd72 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 25 Oct 2024 01:55:24 +0300 Subject: [PATCH 15/29] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 572ed267..33f4807f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Migrated faucet from actix-web to axum (#511). - Changed the `BlockWitness` to pass the inputs to the VM using only advice provider (#516). - [BREAKING] Improved store API errors (return "not found" instead of "internal error" status if requested account(s) not found) (#518). +- [BREAKING] Migrated to v0.11 version of Miden VM (#528). ## 0.5.1 (2024-09-12) From bf9bdc24d42153f1d5a9cf5268d85f5a1c3b6a4d Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 25 Oct 2024 02:32:11 +0300 Subject: [PATCH 16/29] refactor: remove unnecessary async, add warning ignore to makefile --- Cargo.lock | 1 - Makefile | 2 +- bin/faucet/Cargo.toml | 1 - bin/faucet/src/client.rs | 3 --- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c5462a8..b2cef63e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1491,7 +1491,6 @@ dependencies = [ "tower 0.5.1", "tower-http 0.6.1", "tracing", - "winter-maybe-async 0.10.1", ] [[package]] diff --git a/Makefile b/Makefile index afa1a2da..b10ad72a 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ BUILD_PROTO=BUILD_PROTO=1 .PHONY: clippy clippy: ## Runs Clippy with configs - cargo clippy --locked --workspace --all-targets --all-features -- -D warnings + cargo clippy --locked --workspace --all-targets --all-features -- -D warnings --allow clippy::arc_with_non_send_sync .PHONY: fix diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index 4b9b865c..fc03a81a 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -39,7 +39,6 @@ tonic = { workspace = true } tower = "0.5" tower-http = { version = "0.6", features = ["cors", "set-header", "trace"] } tracing = { workspace = true } -winter-maybe-async = { version = "0.10" } [build-dependencies] # Required to inject build metadata. diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index a6d36c2b..ead7d03f 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -30,7 +30,6 @@ use miden_tx::{ use rand::{rngs::StdRng, thread_rng, Rng}; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; use tonic::transport::Channel; -use winter_maybe_async::*; use crate::{ config::FaucetConfig, @@ -204,9 +203,7 @@ impl FaucetDataStore { unsafe impl Send for FaucetDataStore {} unsafe impl Sync for FaucetDataStore {} -#[maybe_async_trait] impl DataStore for FaucetDataStore { - #[maybe_async] fn get_transaction_inputs( &self, account_id: AccountId, From 349c43be3975091d448833ea524b7838af6b66bb Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:42:54 +0500 Subject: [PATCH 17/29] refactor: update genesis generation, read faucet account from `faucet.mac`, request faucet state on initialization --- .github/workflows/deploy_package.yml | 2 +- Cargo.lock | 1 + bin/faucet/src/client.rs | 173 ++++++++---------------- bin/faucet/src/config.rs | 23 +--- bin/faucet/src/handlers.rs | 82 +++-------- bin/faucet/src/main.rs | 13 +- bin/faucet/src/store.rs | 16 +-- bin/node/Cargo.toml | 1 + bin/node/src/commands/genesis/inputs.rs | 6 - bin/node/src/commands/genesis/mod.rs | 74 +++++----- bin/node/src/main.rs | 7 +- config/genesis.toml | 2 - config/miden-faucet.toml | 7 +- 13 files changed, 143 insertions(+), 264 deletions(-) diff --git a/.github/workflows/deploy_package.yml b/.github/workflows/deploy_package.yml index 53530896..d2de0c2b 100644 --- a/.github/workflows/deploy_package.yml +++ b/.github/workflows/deploy_package.yml @@ -117,7 +117,7 @@ jobs: sudo chown -R miden /opt/miden; \ sudo /usr/bin/miden-node init -c /etc/miden/miden-node.toml -g /opt/miden/miden-node/genesis.toml; \ sudo /usr/bin/miden-node make-genesis -i /opt/miden/miden-node/genesis.toml -o /opt/miden/miden-node/genesis.dat --force; \ - sudo /usr/bin/miden-faucet init -c /opt/miden/miden-faucet/miden-faucet.toml + sudo /usr/bin/miden-faucet init -c /opt/miden/miden-faucet/miden-faucet.toml -f /opt/miden/miden-node/accounts/faucet.mac - name: Start miden node service uses: ./.github/actions/ssm_execute diff --git a/Cargo.lock b/Cargo.lock index 5a290bb6..3dfab751 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1566,6 +1566,7 @@ dependencies = [ "miden-node-store", "miden-node-utils", "miden-objects", + "rand", "rand_chacha", "serde", "tokio", diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 36367b3a..4682bba9 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -1,15 +1,7 @@ -use std::{ - path::Path, - rc::Rc, - sync::{Arc, RwLock}, - time::Duration, -}; +use std::{rc::Rc, time::Duration}; -use anyhow::{anyhow, Context}; -use miden_lib::{ - accounts::faucets::create_basic_fungible_faucet, notes::create_p2id_note, - transaction::TransactionKernel, AuthScheme, -}; +use anyhow::Context; +use miden_lib::{notes::create_p2id_note, transaction::TransactionKernel}; use miden_node_proto::generated::{ requests::{ GetAccountDetailsRequest, GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, @@ -17,10 +9,9 @@ use miden_node_proto::generated::{ rpc::api_client::ApiClient, }; use miden_objects::{ - accounts::{Account, AccountId, AccountStorageMode, AuthSecretKey}, - assets::{FungibleAsset, TokenSymbol}, + accounts::{Account, AccountData, AccountId, AuthSecretKey}, + assets::FungibleAsset, crypto::{ - dsa::rpo_falcon512::SecretKey, merkle::{MmrPeaks, PartialMmr}, rand::RpoRandomCoin, }, @@ -28,20 +19,21 @@ use miden_objects::{ transaction::{ChainMmr, ExecutedTransaction, TransactionArgs, TransactionScript}, utils::Deserializable, vm::AdviceMap, - BlockHeader, Felt, Word, + BlockHeader, Felt, }; use miden_tx::{ auth::BasicAuthenticator, utils::Serializable, LocalTransactionProver, ProvingOptions, TransactionExecutor, TransactionProver, }; -use rand::{rngs::StdRng, thread_rng, Rng}; -use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; +use rand::{random, rngs::StdRng}; use tonic::transport::Channel; +use tracing::info; use crate::{ config::FaucetConfig, errors::{ClientError, ImplError}, store::FaucetDataStore, + COMPONENT, }; pub const DISTRIBUTE_FUNGIBLE_ASSET_SCRIPT: &str = @@ -65,25 +57,36 @@ unsafe impl Send for FaucetClient {} impl FaucetClient { /// Creates a new faucet client. pub async fn new(config: &FaucetConfig) -> Result { - let (rpc_api, root_block_header, root_chain_mmr) = initialize_faucet_client(config).await?; - let init_seed: [u8; 32] = [0; 32]; - let (auth_scheme, authenticator) = init_authenticator(init_seed, &config.secret_key_path) - .context("Failed to initialize authentication scheme")?; - - let (faucet_account, account_seed) = build_account(config, init_seed, auth_scheme)?; - let id = faucet_account.id(); - - let data_store = FaucetDataStore::new( - Arc::new(RwLock::new(faucet_account)), - account_seed, - root_block_header, - root_chain_mmr, + let (mut rpc_api, root_block_header, root_chain_mmr) = + initialize_faucet_client(config).await?; + + let faucet_account_data = AccountData::read(&config.faucet_account_path) + .context("Failed to load faucet account from file")?; + + let id = faucet_account_data.account.id(); + + let public_key = match &faucet_account_data.auth_secret_key { + AuthSecretKey::RpoFalcon512(secret) => secret.public_key(), + }; + + let authenticator = BasicAuthenticator::::new(&[( + public_key.into(), + faucet_account_data.auth_secret_key, + )]); + + info!(target: COMPONENT, "Requesting account state from the node..."); + let faucet_account = request_account_state(&mut rpc_api, id).await?; + info!( + target: COMPONENT, + got_new_hash = %faucet_account.hash(), + "Received faucet account state from the node", ); + let data_store = FaucetDataStore::new(faucet_account, root_block_header, root_chain_mmr); + let executor = TransactionExecutor::new(data_store.clone(), Some(Rc::new(authenticator))); - let mut rng = thread_rng(); - let coin_seed: [u64; 4] = rng.gen(); + let coin_seed: [u64; 4] = random(); let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); Ok(Self { data_store, rpc_api, executor, id, rng }) @@ -155,29 +158,6 @@ impl FaucetClient { Ok(response.into_inner().block_height) } - /// Requests faucet account state from the node. - /// - /// The account is expected to be public, otherwise, the error is returned. - pub async fn request_account_state(&mut self) -> Result<(Account, u32), ClientError> { - let account_info = self - .rpc_api - .get_account_details(GetAccountDetailsRequest { account_id: Some(self.id.into()) }) - .await - .context("Failed to get faucet account state")? - .into_inner() - .details - .context("Account info field is empty")?; - - let faucet_account_state_bytes = - account_info.details.context("Account details field is empty")?; - let faucet_account = Account::read_from_bytes(&faucet_account_state_bytes) - .map_err(ImplError) - .context("Failed to deserialize faucet account")?; - let block_num = account_info.summary.context("Account summary field is empty")?.block_num; - - Ok((faucet_account, block_num)) - } - /// Returns a reference to the data store. pub fn data_store(&self) -> &FaucetDataStore { &self.data_store @@ -192,65 +172,6 @@ impl FaucetClient { // HELPER FUNCTIONS // ================================================================================================ -/// Initializes the keypair used to sign transactions. -/// -/// If the secret key file exists, it is read from the file. Otherwise, a new key is generated and -/// written to the file. -fn init_authenticator( - init_seed: [u8; 32], - secret_key_path: impl AsRef, -) -> Result<(AuthScheme, BasicAuthenticator), ClientError> { - // Load secret key from file or generate new one - let secret = if secret_key_path.as_ref().exists() { - SecretKey::read_from_bytes( - &std::fs::read(secret_key_path).context("Failed to read secret key from file")?, - ) - .map_err(ImplError) - .context("Failed to deserialize secret key")? - } else { - let mut rng = ChaCha20Rng::from_seed(init_seed); - let secret = SecretKey::with_rng(&mut rng); - std::fs::write(secret_key_path, secret.to_bytes()) - .context("Failed to write secret key to file")?; - - secret - }; - - let auth_scheme = AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }; - - let authenticator = BasicAuthenticator::::new(&[( - secret.public_key().into(), - AuthSecretKey::RpoFalcon512(secret), - )]); - - Ok((auth_scheme, authenticator)) -} - -/// Builds a new faucet account with the provided configuration. -/// -/// Returns the created account, its seed, and the secret key used to sign transactions. -fn build_account( - config: &FaucetConfig, - init_seed: [u8; 32], - auth_scheme: AuthScheme, -) -> Result<(Account, Word), ClientError> { - let token_symbol = TokenSymbol::new(config.token_symbol.as_str()) - .context("Failed to parse token symbol from configuration file")?; - - let (faucet_account, account_seed) = create_basic_fungible_faucet( - init_seed, - token_symbol, - config.decimals, - Felt::try_from(config.max_supply) - .map_err(|err| anyhow!("Error converting max supply to Felt: {err}"))?, - AccountStorageMode::Public, - auth_scheme, - ) - .context("Failed to create basic fungible faucet account")?; - - Ok((faucet_account, account_seed)) -} - /// Initializes the faucet client by connecting to the node and fetching the root block header. pub async fn initialize_faucet_client( config: &FaucetConfig, @@ -288,6 +209,30 @@ pub async fn initialize_faucet_client( Ok((rpc_api, root_block_header, root_chain_mmr)) } +/// Requests account state from the node. +/// +/// The account is expected to be public, otherwise, the error is returned. +async fn request_account_state( + rpc_api: &mut ApiClient, + account_id: AccountId, +) -> Result { + let account_info = rpc_api + .get_account_details(GetAccountDetailsRequest { account_id: Some(account_id.into()) }) + .await + .context("Failed to get faucet account state")? + .into_inner() + .details + .context("Account info field is empty")?; + + let faucet_account_state_bytes = + account_info.details.context("Account details field is empty")?; + + Account::read_from_bytes(&faucet_account_state_bytes) + .map_err(ImplError) + .context("Failed to deserialize faucet account") + .map_err(Into::into) +} + /// Builds transaction arguments for the mint transaction. fn build_transaction_arguments( output_note: &Note, diff --git a/bin/faucet/src/config.rs b/bin/faucet/src/config.rs index 631f1df9..1fb8947a 100644 --- a/bin/faucet/src/config.rs +++ b/bin/faucet/src/config.rs @@ -9,8 +9,8 @@ use serde::{Deserialize, Serialize}; // Faucet config // ================================================================================================ -/// Default path to the secret key file -const DEFAULT_SECRET_KEY_PATH: &str = "faucet-secret.key"; +/// Default path to the faucet account file +pub const DEFAULT_FAUCET_ACCOUNT_PATH: &str = "accounts/faucet.mac"; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -23,21 +23,15 @@ pub struct FaucetConfig { pub timeout_ms: u64, /// Possible options on the amount of asset that should be dispersed on each faucet request pub asset_amount_options: Vec, - /// Token symbol of the generated fungible asset - pub token_symbol: String, - /// Number of decimals of the generated fungible asset - pub decimals: u8, - /// Maximum supply of the generated fungible asset - pub max_supply: u64, - /// Path to the key store file - pub secret_key_path: PathBuf, + /// Path to the faucet account file + pub faucet_account_path: PathBuf, } impl Display for FaucetConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( - "{{ endpoint: \"{}\", asset_amount_options: {:?}, token_symbol: {}, decimals: {}, max_supply: {} }}", - self.endpoint, self.asset_amount_options, self.token_symbol, self.decimals, self.max_supply + "{{ endpoint: \"{}\", node_url: \"{}\", timeout_ms: \"{}\", asset_amount_options: {:?}, faucet_account_path: \"{}\" }}", + self.endpoint, self.node_url, self.timeout_ms, self.asset_amount_options, self.faucet_account_path.display() )) } } @@ -49,10 +43,7 @@ impl Default for FaucetConfig { node_url: Endpoint::localhost(DEFAULT_NODE_RPC_PORT).to_string(), timeout_ms: 10000, asset_amount_options: vec![100, 500, 1000], - token_symbol: "POL".to_string(), - decimals: 8, - max_supply: 1000000, - secret_key_path: DEFAULT_SECRET_KEY_PATH.into(), + faucet_account_path: DEFAULT_FAUCET_ACCOUNT_PATH.into(), } } } diff --git a/bin/faucet/src/handlers.rs b/bin/faucet/src/handlers.rs index 5b4ddd97..764f997d 100644 --- a/bin/faucet/src/handlers.rs +++ b/bin/faucet/src/handlers.rs @@ -14,7 +14,7 @@ use miden_objects::{ }; use serde::{Deserialize, Serialize}; use tonic::body; -use tracing::{error, info}; +use tracing::info; use crate::{errors::HandlerError, state::FaucetState, COMPONENT}; @@ -65,67 +65,25 @@ pub async fn get_tokens( let target_account_id = AccountId::from_hex(req.account_id.as_str()) .map_err(|err| HandlerError::BadRequest(err.to_string()))?; - let (created_note, block_height) = loop { - let mut faucet_account = client.data_store().faucet_account(); - - // Execute transaction - info!(target: COMPONENT, "Executing mint transaction for account."); - let (executed_tx, created_note) = client.execute_mint_transaction( - target_account_id, - req.is_private_note, - req.asset_amount, - )?; - - let prev_hash = faucet_account.hash(); - - faucet_account - .apply_delta(executed_tx.account_delta()) - .context("Failed to apply faucet account delta")?; - - let new_hash = faucet_account.hash(); - - // Run transaction prover & send transaction to node - info!(target: COMPONENT, "Proving and submitting transaction."); - match client.prove_and_submit_transaction(executed_tx.clone()).await { - Ok(block_height) => { - break (created_note, block_height); - }, - Err(err) => { - error!( - target: COMPONENT, - %err, - "Failed to prove and submit transaction", - ); - - // TODO: Improve error statuses returned from the `SubmitProvenTransaction` endpoint - // of block producer. Check received error status here. - - info!(target: COMPONENT, "Requesting account state from the node..."); - let (got_faucet_account, block_num) = client.request_account_state().await?; - let got_new_hash = got_faucet_account.hash(); - info!( - target: COMPONENT, - %prev_hash, - %new_hash, - %got_new_hash, - "Received new account state from the node", - ); - // If the hash hasn't changed, then the account's state we had is correct, - // and we should not try to execute the transaction again. We can just return error - // to the caller. - if new_hash == prev_hash { - return Err(err.into()); - } - // If the new hash from the node is the same, as we expected to have, then - // transaction was successfully executed despite the error. Don't need to retry. - if new_hash == got_new_hash { - break (created_note, block_num); - } - - client.data_store().update_faucet_state(got_faucet_account).await?; - }, - } - }; + // Execute transaction + info!(target: COMPONENT, "Executing mint transaction for account."); + let (executed_tx, created_note) = client.execute_mint_transaction( + target_account_id, + req.is_private_note, + req.asset_amount, + )?; + + let mut faucet_account = client.data_store().faucet_account(); + faucet_account + .apply_delta(executed_tx.account_delta()) + .context("Failed to apply faucet account delta")?; + + // Run transaction prover & send transaction to node + info!(target: COMPONENT, "Proving and submitting transaction."); + let block_height = client.prove_and_submit_transaction(executed_tx).await?; + + // Update data store with the new faucet state + client.data_store().update_faucet_state(faucet_account).await?; let note_id: NoteId = created_note.id(); let note_details = diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 2188d4c5..72e8f3de 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -22,7 +22,7 @@ use tower_http::{cors::CorsLayer, set_header::SetResponseHeaderLayer, trace::Tra use tracing::info; use crate::{ - config::FaucetConfig, + config::{FaucetConfig, DEFAULT_FAUCET_ACCOUNT_PATH}, handlers::{get_index, get_metadata, get_static_file, get_tokens}, }; // CONSTANTS @@ -53,6 +53,8 @@ pub enum Command { Init { #[arg(short, long, default_value = FAUCET_CONFIG_FILE_PATH)] config_path: String, + #[arg(short, long, default_value = DEFAULT_FAUCET_ACCOUNT_PATH)] + faucet_account_path: String, }, } @@ -102,12 +104,17 @@ async fn main() -> anyhow::Result<()> { axum::serve(listener, app).await.unwrap(); }, - Command::Init { config_path } => { + Command::Init { config_path, faucet_account_path } => { let current_dir = std::env::current_dir().context("failed to open current directory")?; let config_file_path = current_dir.join(config_path); - let config = FaucetConfig::default(); + + let config = FaucetConfig { + faucet_account_path: faucet_account_path.into(), + ..FaucetConfig::default() + }; + let config_as_toml_string = toml::to_string(&config).context("Failed to serialize default config")?; diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 8b1b15ec..9dbf22bf 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -4,7 +4,7 @@ use miden_objects::{ accounts::{Account, AccountId}, notes::NoteId, transaction::{ChainMmr, InputNotes, TransactionInputs}, - BlockHeader, Word, + BlockHeader, }; use miden_tx::{DataStore, DataStoreError}; @@ -13,8 +13,6 @@ use crate::errors::HandlerError; #[derive(Clone)] pub struct FaucetDataStore { faucet_account: Arc>, - /// Seed used for faucet account creation. - seed: Word, block_header: BlockHeader, chain_mmr: ChainMmr, } @@ -24,14 +22,12 @@ pub struct FaucetDataStore { impl FaucetDataStore { pub fn new( - faucet_account: Arc>, - seed: Word, + faucet_account: Account, root_block_header: BlockHeader, root_chain_mmr: ChainMmr, ) -> Self { Self { - faucet_account, - seed, + faucet_account: Arc::new(RwLock::new(faucet_account)), block_header: root_block_header, chain_mmr: root_chain_mmr, } @@ -62,14 +58,12 @@ impl DataStore for FaucetDataStore { return Err(DataStoreError::AccountNotFound(account_id)); } - let empty_input_notes = InputNotes::new(vec![]).expect("Empty notes must succeed"); - TransactionInputs::new( account.clone(), - account.is_new().then_some(self.seed), + None, self.block_header, self.chain_mmr.clone(), - empty_input_notes, + InputNotes::default(), ) .map_err(DataStoreError::InvalidTransactionInput) } diff --git a/bin/node/Cargo.toml b/bin/node/Cargo.toml index d544f285..f1f2bec9 100644 --- a/bin/node/Cargo.toml +++ b/bin/node/Cargo.toml @@ -26,6 +26,7 @@ miden-node-rpc = { workspace = true } miden-node-store = { workspace = true } miden-node-utils = { workspace = true } miden-objects = { workspace = true } +rand = "0.8" rand_chacha = "0.3" serde = { version = "1.0", features = ["derive"] } tokio = { workspace = true, features = ["rt-multi-thread", "net", "macros"] } diff --git a/bin/node/src/commands/genesis/inputs.rs b/bin/node/src/commands/genesis/inputs.rs index a400fc69..2f93d1d6 100644 --- a/bin/node/src/commands/genesis/inputs.rs +++ b/bin/node/src/commands/genesis/inputs.rs @@ -23,9 +23,7 @@ pub enum AccountInput { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BasicFungibleFaucetInputs { - pub init_seed: String, pub auth_scheme: AuthSchemeInput, - pub auth_seed: String, pub token_symbol: String, pub decimals: u8, pub max_supply: u64, @@ -46,11 +44,7 @@ impl Default for GenesisInput { .expect("Current timestamp should be greater than unix epoch") .as_secs() as u32, accounts: Some(vec![AccountInput::BasicFungibleFaucet(BasicFungibleFaucetInputs { - init_seed: "0xc123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - .to_string(), auth_scheme: AuthSchemeInput::RpoFalcon512, - auth_seed: "0xd123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" - .to_string(), token_symbol: "POL".to_string(), decimals: 12, max_supply: 1000000, diff --git a/bin/node/src/commands/genesis/mod.rs b/bin/node/src/commands/genesis/mod.rs index 3772b120..a5ed2922 100644 --- a/bin/node/src/commands/genesis/mod.rs +++ b/bin/node/src/commands/genesis/mod.rs @@ -9,15 +9,15 @@ use miden_lib::{accounts::faucets::create_basic_fungible_faucet, AuthScheme}; use miden_node_store::genesis::GenesisState; use miden_node_utils::config::load_config; use miden_objects::{ - accounts::{Account, AccountData, AccountStorageMode, AuthSecretKey}, + accounts::{Account, AccountData, AuthSecretKey}, assets::TokenSymbol, - crypto::{ - dsa::rpo_falcon512::SecretKey, - rand::RpoRandomCoin, - utils::{hex_to_bytes, Serializable}, - }, + crypto::{dsa::rpo_falcon512::SecretKey, rand::RpoRandomCoin, utils::Serializable}, Digest, Felt, ONE, }; +use rand_chacha::{ + rand_core::{RngCore, SeedableRng}, + ChaCha20Rng, +}; use tracing::info; mod inputs; @@ -80,13 +80,9 @@ pub fn make_genesis(inputs_path: &PathBuf, output_path: &PathBuf, force: &bool) })?; info!("Genesis input file: {} has successfully been loaded.", inputs_path.display()); + let accounts_path = parent_path.join(DEFAULT_ACCOUNTS_DIR); let accounts = - create_accounts(&genesis_input.accounts.unwrap_or_default(), parent_path, force)?; - info!( - "Accounts have successfully been created at: {}/{}", - parent_path.display(), - DEFAULT_ACCOUNTS_DIR - ); + create_accounts(&genesis_input.accounts.unwrap_or_default(), &accounts_path, force)?; let genesis_state = GenesisState::new(accounts, genesis_input.version, genesis_input.timestamp); fs::write(output_path, genesis_state.to_bytes()).unwrap_or_else(|_| { @@ -102,13 +98,10 @@ pub fn make_genesis(inputs_path: &PathBuf, output_path: &PathBuf, force: &bool) /// This function also writes the account data files into the default accounts directory. fn create_accounts( accounts: &[AccountInput], - parent_path: &Path, + accounts_path: impl AsRef, force: &bool, ) -> Result> { - let mut accounts_path = PathBuf::from(&parent_path); - accounts_path.push(DEFAULT_ACCOUNTS_DIR); - - if accounts_path.try_exists()? { + if accounts_path.as_ref().try_exists()? { if !force { bail!( "Failed to create accounts directory because it already exists. \ @@ -121,19 +114,21 @@ fn create_accounts( fs::create_dir_all(&accounts_path).context("Failed to create accounts directory")?; let mut final_accounts = Vec::new(); + let mut faucet_count = 0; + let mut rng = ChaCha20Rng::from_seed(rand::random()); for account in accounts { // build offchain account data from account inputs - let mut account_data = match account { + let (mut account_data, name) = match account { AccountInput::BasicFungibleFaucet(inputs) => { info!("Creating fungible faucet account..."); - let init_seed = hex_to_bytes(&inputs.init_seed)?; - - let (auth_scheme, auth_secret_key) = - parse_auth_inputs(inputs.auth_scheme, &inputs.auth_seed)?; - - let storage_mode: AccountStorageMode = inputs.storage_mode.as_str().try_into()?; + let mut auth_seed = [0; 32]; + rng.fill_bytes(&mut auth_seed); + let (auth_scheme, auth_secret_key) = gen_auth_keys(inputs.auth_scheme, auth_seed)?; + let mut init_seed = [0; 32]; + rng.fill_bytes(&mut init_seed); + let storage_mode = inputs.storage_mode.as_str().try_into()?; let (account, account_seed) = create_basic_fungible_faucet( init_seed, TokenSymbol::try_from(inputs.token_symbol.as_str())?, @@ -144,23 +139,29 @@ fn create_accounts( auth_scheme, )?; - AccountData::new(account, Some(account_seed), auth_secret_key) + let name = if faucet_count == 0 { + "faucet".to_string() + } else { + format!("faucet{faucet_count}") + }; + faucet_count += 1; + + (AccountData::new(account, Some(account_seed), auth_secret_key), name) }, }; // write account data to file - let path = format!("{}/account{}.mac", accounts_path.display(), final_accounts.len()); - let path = Path::new(&path); + let path = accounts_path.as_ref().join(format!("{name}.mac")); - if let Ok(path_exists) = path.try_exists() { - if path_exists && !force { - bail!("Failed to generate account file {} because it already exists. Use the --force flag to overwrite.", path.display()); - } + if !force && matches!(path.try_exists(), Ok(true)) { + bail!("Failed to generate account file {} because it already exists. Use the --force flag to overwrite.", path.display()); } account_data.account.set_nonce(ONE)?; - account_data.write(path)?; + account_data.write(&path)?; + + info!("Account \"{name}\" has successfully been saved to: {}", path.display()); final_accounts.push(account_data.account); } @@ -168,13 +169,12 @@ fn create_accounts( Ok(final_accounts) } -fn parse_auth_inputs( +fn gen_auth_keys( auth_scheme_input: AuthSchemeInput, - auth_seed: &str, + auth_seed: [u8; 32], ) -> Result<(AuthScheme, AuthSecretKey)> { match auth_scheme_input { AuthSchemeInput::RpoFalcon512 => { - let auth_seed: [u8; 32] = hex_to_bytes(auth_seed)?; let rng_seed = Digest::try_from(&auth_seed)?.into(); let mut rng = RpoRandomCoin::new(rng_seed); let secret = SecretKey::with_rng(&mut rng); @@ -215,9 +215,7 @@ mod tests { [[accounts]] type = "BasicFungibleFaucet" - init_seed = "0xc123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" auth_scheme = "RpoFalcon512" - auth_seed = "0xd123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" token_symbol = "POL" decimals = 12 max_supply = 1000000 @@ -230,7 +228,7 @@ mod tests { // run make_genesis to generate genesis.dat and accounts folder and files make_genesis(&genesis_inputs_file_path, &genesis_dat_file_path, &true).unwrap(); - let a0_file_path = PathBuf::from("accounts/account0.mac"); + let a0_file_path = PathBuf::from("accounts/faucet.mac"); // assert that the genesis.dat and account files exist assert!(genesis_dat_file_path.exists()); diff --git a/bin/node/src/main.rs b/bin/node/src/main.rs index 1ce93c60..1839d748 100644 --- a/bin/node/src/main.rs +++ b/bin/node/src/main.rs @@ -127,11 +127,8 @@ async fn main() -> anyhow::Result<()> { let current_dir = std::env::current_dir() .map_err(|err| anyhow!("failed to open current directory: {err}"))?; - let mut config = current_dir.clone(); - let mut genesis = current_dir.clone(); - - config.push(config_path); - genesis.push(genesis_path); + let config = current_dir.join(config_path); + let genesis = current_dir.join(genesis_path); init_config_files(config, genesis) }, diff --git a/config/genesis.toml b/config/genesis.toml index 6a77d9a7..8c74a4dd 100644 --- a/config/genesis.toml +++ b/config/genesis.toml @@ -5,9 +5,7 @@ timestamp = 1672531200 [[accounts]] type = "BasicFungibleFaucet" storage_mode = "public" -init_seed = "0xc123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" auth_scheme = "RpoFalcon512" -auth_seed = "0xd123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" token_symbol = "POL" decimals = 12 max_supply = 1000000 diff --git a/config/miden-faucet.toml b/config/miden-faucet.toml index 688e2ac4..8124e33d 100644 --- a/config/miden-faucet.toml +++ b/config/miden-faucet.toml @@ -1,10 +1,5 @@ endpoint = { host = "localhost", port = 8080 } node_url = "http://localhost:57291" timeout_ms = 10000 -secret_key_path = "faucet-secret.key" - -# Data used to construct the faucet account of the faucet asset_amount_options = [100, 500, 1000] -token_symbol = "POL" -decimals = 8 -max_supply = 1000000 +faucet_account_path = "accounts/faucet.mac" From bf0e97d1a4bef0d754628ba997414caa0f7c33fc Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:47:11 +0500 Subject: [PATCH 18/29] feat: faucet account creation --- Cargo.lock | 1 + Cargo.toml | 1 + bin/faucet/Cargo.toml | 2 +- bin/faucet/src/client.rs | 35 ++++++++++--- bin/faucet/src/config.rs | 5 +- bin/faucet/src/errors.rs | 9 +++- bin/faucet/src/main.rs | 78 +++++++++++++++++++++++----- bin/faucet/src/store.rs | 16 +++--- bin/node/Cargo.toml | 2 +- bin/node/src/commands/genesis/mod.rs | 34 +++++------- crates/utils/Cargo.toml | 1 + crates/utils/src/crypto.rs | 13 +++++ crates/utils/src/lib.rs | 1 + 13 files changed, 146 insertions(+), 52 deletions(-) create mode 100644 crates/utils/src/crypto.rs diff --git a/Cargo.lock b/Cargo.lock index 3dfab751..9c0a245b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1685,6 +1685,7 @@ dependencies = [ "figment", "itertools 0.12.1", "miden-objects", + "rand", "serde", "thiserror", "tonic", diff --git a/Cargo.toml b/Cargo.toml index 656515f3..3987e168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ miden-processor = { version = "0.10" } miden-stdlib = { version = "0.10", default-features = false } miden-tx = { git = "https://github.com/0xPolygonMiden/miden-base.git", branch = "next" } prost = { version = "0.13" } +rand = { version = "0.8" } thiserror = { version = "1.0" } tokio = { version = "1.40", features = ["rt-multi-thread"] } tokio-stream = { version = "0.1" } diff --git a/bin/faucet/Cargo.toml b/bin/faucet/Cargo.toml index 2d047d95..7d299e3e 100644 --- a/bin/faucet/Cargo.toml +++ b/bin/faucet/Cargo.toml @@ -29,7 +29,7 @@ miden-node-utils = { workspace = true } miden-objects = { workspace = true , features = ["concurrent"] } miden-tx = { workspace = true, features = ["concurrent"] } mime = "0.3" -rand = { version = "0.8" } +rand = { workspace = true } rand_chacha = "0.3" serde = { version = "1.0", features = ["derive"] } static-files = "0.2" diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 4682bba9..5a8bca01 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -75,14 +75,35 @@ impl FaucetClient { )]); info!(target: COMPONENT, "Requesting account state from the node..."); - let faucet_account = request_account_state(&mut rpc_api, id).await?; - info!( - target: COMPONENT, - got_new_hash = %faucet_account.hash(), - "Received faucet account state from the node", - ); + let faucet_account = match request_account_state(&mut rpc_api, id).await { + Ok(account) => { + info!( + target: COMPONENT, + hash = %account.hash(), + nonce = %account.nonce(), + "Received faucet account state from the node", + ); + + account + }, + Err(err) => match err { + ClientError::RequestError(status) if status.code() == tonic::Code::NotFound => { + info!(target: COMPONENT, "Faucet account not found in the node"); + + faucet_account_data.account + }, + _ => { + return Err(err); + }, + }, + }; - let data_store = FaucetDataStore::new(faucet_account, root_block_header, root_chain_mmr); + let data_store = FaucetDataStore::new( + faucet_account, + faucet_account_data.account_seed, + root_block_header, + root_chain_mmr, + ); let executor = TransactionExecutor::new(data_store.clone(), Some(Rc::new(authenticator))); diff --git a/bin/faucet/src/config.rs b/bin/faucet/src/config.rs index 1fb8947a..d61fe5cb 100644 --- a/bin/faucet/src/config.rs +++ b/bin/faucet/src/config.rs @@ -12,6 +12,9 @@ use serde::{Deserialize, Serialize}; /// Default path to the faucet account file pub const DEFAULT_FAUCET_ACCOUNT_PATH: &str = "accounts/faucet.mac"; +/// Default timeout for RPC requests +pub const DEFAULT_RPC_TIMEOUT_MS: u64 = 10000; + #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct FaucetConfig { @@ -41,7 +44,7 @@ impl Default for FaucetConfig { Self { endpoint: Endpoint::localhost(DEFAULT_FAUCET_SERVER_PORT), node_url: Endpoint::localhost(DEFAULT_NODE_RPC_PORT).to_string(), - timeout_ms: 10000, + timeout_ms: DEFAULT_RPC_TIMEOUT_MS, asset_amount_options: vec![100, 500, 1000], faucet_account_path: DEFAULT_FAUCET_ACCOUNT_PATH.into(), } diff --git a/bin/faucet/src/errors.rs b/bin/faucet/src/errors.rs index e2d393a0..9b06c1d1 100644 --- a/bin/faucet/src/errors.rs +++ b/bin/faucet/src/errors.rs @@ -13,8 +13,13 @@ use thiserror::Error; pub struct ImplError(pub E); #[derive(Debug, Error)] -#[error("Client error: {0:#}")] -pub struct ClientError(#[from] anyhow::Error); +pub enum ClientError { + #[error("Request error: {0:#}")] + RequestError(#[from] tonic::Status), + + #[error("Client error: {0:#}")] + Other(#[from] anyhow::Error), +} #[derive(Debug, Error)] pub enum HandlerError { diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index 72e8f3de..c5ce2c87 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -5,7 +5,7 @@ mod handlers; mod state; mod store; -use std::{fs::File, io::Write, path::PathBuf}; +use std::path::PathBuf; use anyhow::Context; use axum::{ @@ -14,7 +14,16 @@ use axum::{ }; use clap::{Parser, Subcommand}; use http::HeaderValue; -use miden_node_utils::{config::load_config, version::LongVersion}; +use miden_lib::{accounts::faucets::create_basic_fungible_faucet, AuthScheme}; +use miden_node_utils::{config::load_config, crypto::get_rpo_random_coin, version::LongVersion}; +use miden_objects::{ + accounts::{AccountData, AccountStorageMode, AuthSecretKey}, + assets::TokenSymbol, + crypto::dsa::rpo_falcon512::SecretKey, + Felt, +}; +use rand::Rng; +use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; use state::FaucetState; use tokio::net::TcpListener; use tower::ServiceBuilder; @@ -25,6 +34,7 @@ use crate::{ config::{FaucetConfig, DEFAULT_FAUCET_ACCOUNT_PATH}, handlers::{get_index, get_metadata, get_static_file, get_tokens}, }; + // CONSTANTS // ================================================================================================= @@ -49,7 +59,19 @@ pub enum Command { config: PathBuf, }, - /// Generates default configuration file for the faucet + /// Create a new public faucet account and save to the file + CreateFaucetAccount { + #[arg(short, long, value_name = "FILE", default_value = DEFAULT_FAUCET_ACCOUNT_PATH)] + output_path: PathBuf, + #[arg(short, long)] + token_symbol: String, + #[arg(short, long)] + decimals: u8, + #[arg(short, long)] + max_supply: u64, + }, + + /// Generate default configuration file for the faucet Init { #[arg(short, long, default_value = FAUCET_CONFIG_FILE_PATH)] config_path: String, @@ -104,9 +126,46 @@ async fn main() -> anyhow::Result<()> { axum::serve(listener, app).await.unwrap(); }, + + Command::CreateFaucetAccount { + output_path, + token_symbol, + decimals, + max_supply, + } => { + let current_dir = + std::env::current_dir().context("Failed to open current directory")?; + + let mut rng = ChaCha20Rng::from_seed(rand::random()); + + let secret = SecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); + + let (account, account_seed) = create_basic_fungible_faucet( + rng.gen(), + TokenSymbol::try_from(token_symbol.as_str()) + .context("Failed to parse token symbol")?, + *decimals, + Felt::try_from(*max_supply) + .expect("max supply value is greater than or equal to the field modulus"), + AccountStorageMode::Public, + AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }, + ) + .context("Failed to create basic fungible faucet account")?; + + let account_data = + AccountData::new(account, Some(account_seed), AuthSecretKey::RpoFalcon512(secret)); + + let output_path = current_dir.join(output_path); + account_data + .write(&output_path) + .context("Failed to write account data to file")?; + + println!("Faucet account file successfully created at: {output_path:?}"); + }, + Command::Init { config_path, faucet_account_path } => { let current_dir = - std::env::current_dir().context("failed to open current directory")?; + std::env::current_dir().context("Failed to open current directory")?; let config_file_path = current_dir.join(config_path); @@ -118,15 +177,8 @@ async fn main() -> anyhow::Result<()> { let config_as_toml_string = toml::to_string(&config).context("Failed to serialize default config")?; - let mut file_handle = File::options() - .write(true) - .create_new(true) - .open(&config_file_path) - .context("Error opening configuration file")?; - - file_handle - .write(config_as_toml_string.as_bytes()) - .context("Error writing to file")?; + std::fs::write(&config_file_path, config_as_toml_string) + .context("Error writing config to file")?; println!("Config file successfully created at: {config_file_path:?}"); }, diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 9dbf22bf..38406997 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -4,7 +4,7 @@ use miden_objects::{ accounts::{Account, AccountId}, notes::NoteId, transaction::{ChainMmr, InputNotes, TransactionInputs}, - BlockHeader, + BlockHeader, Word, }; use miden_tx::{DataStore, DataStoreError}; @@ -13,6 +13,8 @@ use crate::errors::HandlerError; #[derive(Clone)] pub struct FaucetDataStore { faucet_account: Arc>, + /// Optional initial seed used for faucet account creation. + init_seed: Option, block_header: BlockHeader, chain_mmr: ChainMmr, } @@ -23,13 +25,15 @@ pub struct FaucetDataStore { impl FaucetDataStore { pub fn new( faucet_account: Account, - root_block_header: BlockHeader, - root_chain_mmr: ChainMmr, + init_seed: Option, + block_header: BlockHeader, + chain_mmr: ChainMmr, ) -> Self { Self { faucet_account: Arc::new(RwLock::new(faucet_account)), - block_header: root_block_header, - chain_mmr: root_chain_mmr, + init_seed, + block_header, + chain_mmr, } } @@ -60,7 +64,7 @@ impl DataStore for FaucetDataStore { TransactionInputs::new( account.clone(), - None, + account.is_new().then_some(self.init_seed).flatten(), self.block_header, self.chain_mmr.clone(), InputNotes::default(), diff --git a/bin/node/Cargo.toml b/bin/node/Cargo.toml index f1f2bec9..b855cbe6 100644 --- a/bin/node/Cargo.toml +++ b/bin/node/Cargo.toml @@ -26,7 +26,7 @@ miden-node-rpc = { workspace = true } miden-node-store = { workspace = true } miden-node-utils = { workspace = true } miden-objects = { workspace = true } -rand = "0.8" +rand = { workspace = true } rand_chacha = "0.3" serde = { version = "1.0", features = ["derive"] } tokio = { workspace = true, features = ["rt-multi-thread", "net", "macros"] } diff --git a/bin/node/src/commands/genesis/mod.rs b/bin/node/src/commands/genesis/mod.rs index a5ed2922..cae7d14d 100644 --- a/bin/node/src/commands/genesis/mod.rs +++ b/bin/node/src/commands/genesis/mod.rs @@ -7,17 +7,15 @@ use anyhow::{anyhow, bail, Context, Result}; pub use inputs::{AccountInput, AuthSchemeInput, GenesisInput}; use miden_lib::{accounts::faucets::create_basic_fungible_faucet, AuthScheme}; use miden_node_store::genesis::GenesisState; -use miden_node_utils::config::load_config; +use miden_node_utils::{config::load_config, crypto::get_rpo_random_coin}; use miden_objects::{ accounts::{Account, AccountData, AuthSecretKey}, assets::TokenSymbol, - crypto::{dsa::rpo_falcon512::SecretKey, rand::RpoRandomCoin, utils::Serializable}, - Digest, Felt, ONE, -}; -use rand_chacha::{ - rand_core::{RngCore, SeedableRng}, - ChaCha20Rng, + crypto::{dsa::rpo_falcon512::SecretKey, utils::Serializable}, + Felt, ONE, }; +use rand::Rng; +use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; use tracing::info; mod inputs; @@ -122,15 +120,11 @@ fn create_accounts( let (mut account_data, name) = match account { AccountInput::BasicFungibleFaucet(inputs) => { info!("Creating fungible faucet account..."); - let mut auth_seed = [0; 32]; - rng.fill_bytes(&mut auth_seed); - let (auth_scheme, auth_secret_key) = gen_auth_keys(inputs.auth_scheme, auth_seed)?; + let (auth_scheme, auth_secret_key) = gen_auth_keys(inputs.auth_scheme, &mut rng)?; - let mut init_seed = [0; 32]; - rng.fill_bytes(&mut init_seed); let storage_mode = inputs.storage_mode.as_str().try_into()?; let (account, account_seed) = create_basic_fungible_faucet( - init_seed, + rng.gen(), TokenSymbol::try_from(inputs.token_symbol.as_str())?, inputs.decimals, Felt::try_from(inputs.max_supply) @@ -171,18 +165,16 @@ fn create_accounts( fn gen_auth_keys( auth_scheme_input: AuthSchemeInput, - auth_seed: [u8; 32], + rng: &mut ChaCha20Rng, ) -> Result<(AuthScheme, AuthSecretKey)> { match auth_scheme_input { AuthSchemeInput::RpoFalcon512 => { - let rng_seed = Digest::try_from(&auth_seed)?.into(); - let mut rng = RpoRandomCoin::new(rng_seed); - let secret = SecretKey::with_rng(&mut rng); - - let auth_scheme = AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }; - let auth_secret_key = AuthSecretKey::RpoFalcon512(secret); + let secret = SecretKey::with_rng(&mut get_rpo_random_coin(rng)); - Ok((auth_scheme, auth_secret_key)) + Ok(( + AuthScheme::RpoFalcon512 { pub_key: secret.public_key() }, + AuthSecretKey::RpoFalcon512(secret), + )) }, } } diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index e3b4494d..75f19675 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -20,6 +20,7 @@ anyhow = { version = "1.0" } figment = { version = "0.10", features = ["toml", "env"] } itertools = { version = "0.12" } miden-objects = { workspace = true } +rand = { workspace = true } serde = { version = "1.0", features = ["derive"] } thiserror = { workspace = true } tonic = { workspace = true } diff --git a/crates/utils/src/crypto.rs b/crates/utils/src/crypto.rs new file mode 100644 index 00000000..f8a48aef --- /dev/null +++ b/crates/utils/src/crypto.rs @@ -0,0 +1,13 @@ +use miden_objects::{ + crypto::{hash::rpo::RpoDigest, rand::RpoRandomCoin}, + Felt, +}; +use rand::{Rng, RngCore}; + +/// Creates a new RPO Random Coin with random seed +pub fn get_rpo_random_coin(rng: &mut T) -> RpoRandomCoin { + let auth_seed: [u64; 4] = rng.gen(); + let rng_seed = RpoDigest::from(auth_seed.map(Felt::new)); + + RpoRandomCoin::new(rng_seed.into()) +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index b098bf2d..275ee7e3 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,4 +1,5 @@ pub mod config; +pub mod crypto; pub mod errors; pub mod formatting; pub mod logging; From c9f7fddcbb7ee18211d938403d600f350f8375fd Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:13:31 +0500 Subject: [PATCH 19/29] fix: handling of account not found error --- bin/faucet/src/client.rs | 4 ++-- bin/faucet/src/errors.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 5a8bca01..cebe01cd 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -86,6 +86,7 @@ impl FaucetClient { account }, + Err(err) => match err { ClientError::RequestError(status) if status.code() == tonic::Code::NotFound => { info!(target: COMPONENT, "Faucet account not found in the node"); @@ -239,8 +240,7 @@ async fn request_account_state( ) -> Result { let account_info = rpc_api .get_account_details(GetAccountDetailsRequest { account_id: Some(account_id.into()) }) - .await - .context("Failed to get faucet account state")? + .await? .into_inner() .details .context("Account info field is empty")?; diff --git a/bin/faucet/src/errors.rs b/bin/faucet/src/errors.rs index 9b06c1d1..c8705413 100644 --- a/bin/faucet/src/errors.rs +++ b/bin/faucet/src/errors.rs @@ -14,7 +14,7 @@ pub struct ImplError(pub E); #[derive(Debug, Error)] pub enum ClientError { - #[error("Request error: {0:#}")] + #[error("Request error: {0}")] RequestError(#[from] tonic::Status), #[error("Client error: {0:#}")] From 9e5d91d9513fee184f4cbf65765faf93a7c05caa Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 25 Oct 2024 14:49:49 -0700 Subject: [PATCH 20/29] refactor: use Arc and Mutex --- bin/faucet/src/client.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index ead7d03f..ca879973 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -1,4 +1,7 @@ -use std::{cell::RefCell, rc::Rc, sync::Arc, time::Duration}; +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; use miden_lib::{ accounts::faucets::create_basic_fungible_faucet, notes::create_p2id_note, @@ -47,7 +50,7 @@ pub const DISTRIBUTE_FUNGIBLE_ASSET_SCRIPT: &str = pub struct FaucetClient { rpc_api: ApiClient, executor: TransactionExecutor, - data_store: FaucetDataStore, + data_store: Arc, id: AccountId, rng: RpoRandomCoin, } @@ -61,21 +64,20 @@ impl FaucetClient { initialize_faucet_client(config.clone()).await?; let (faucet_account, account_seed, secret) = build_account(config.clone())?; - let faucet_account = Rc::new(RefCell::new(faucet_account)); - let id = faucet_account.borrow().id(); + let faucet_account = faucet_account; + let id = faucet_account.id(); - let data_store = FaucetDataStore::new( - faucet_account.clone(), + let data_store = Arc::new(FaucetDataStore::new( + faucet_account, account_seed, root_block_header, root_chain_mmr, - ); + )); let authenticator = Arc::new(BasicAuthenticator::::new(&[( secret.public_key().into(), AuthSecretKey::RpoFalcon512(secret), )])) as Arc; - let executor = - TransactionExecutor::new(Arc::new(data_store.clone()), Some(authenticator.clone())); + let executor = TransactionExecutor::new(data_store.clone(), Some(authenticator)); let mut rng = thread_rng(); let coin_seed: [u64; 4] = rng.gen(); @@ -165,9 +167,8 @@ impl FaucetClient { } } -#[derive(Clone)] pub struct FaucetDataStore { - faucet_account: Rc>, + faucet_account: Mutex, seed: Word, block_header: BlockHeader, chain_mmr: ChainMmr, @@ -178,13 +179,13 @@ pub struct FaucetDataStore { impl FaucetDataStore { pub fn new( - faucet_account: Rc>, + faucet_account: Account, seed: Word, root_block_header: BlockHeader, root_chain_mmr: ChainMmr, ) -> Self { Self { - faucet_account, + faucet_account: Mutex::new(faucet_account), seed, block_header: root_block_header, chain_mmr: root_chain_mmr, @@ -192,17 +193,15 @@ impl FaucetDataStore { } /// Updates the stored faucet account with the provided delta. - fn update_faucet_account(&mut self, delta: &AccountDelta) -> Result<(), ProcessError> { + fn update_faucet_account(&self, delta: &AccountDelta) -> Result<(), ProcessError> { self.faucet_account - .borrow_mut() + .lock() + .unwrap() .apply_delta(delta) .map_err(|err| ProcessError::InternalServerError(err.to_string())) } } -unsafe impl Send for FaucetDataStore {} -unsafe impl Sync for FaucetDataStore {} - impl DataStore for FaucetDataStore { fn get_transaction_inputs( &self, @@ -210,7 +209,7 @@ impl DataStore for FaucetDataStore { _block_ref: u32, _notes: &[NoteId], ) -> Result { - let account = self.faucet_account.borrow(); + let account = self.faucet_account.lock().unwrap(); if account_id != account.id() { return Err(DataStoreError::AccountNotFound(account_id)); } From 738eae0f4f7b582f9c99aa0ada5e010db7908029 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 25 Oct 2024 14:56:13 -0700 Subject: [PATCH 21/29] chore: removed unneeded cast --- bin/faucet/src/client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index ca879973..84e0cf5c 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -25,7 +25,7 @@ use miden_objects::{ BlockHeader, Felt, Word, }; use miden_tx::{ - auth::{BasicAuthenticator, TransactionAuthenticator}, + auth::BasicAuthenticator, utils::Serializable, DataStore, DataStoreError, LocalTransactionProver, ProvingOptions, TransactionExecutor, TransactionInputs, TransactionProver, @@ -76,7 +76,7 @@ impl FaucetClient { let authenticator = Arc::new(BasicAuthenticator::::new(&[( secret.public_key().into(), AuthSecretKey::RpoFalcon512(secret), - )])) as Arc; + )])); let executor = TransactionExecutor::new(data_store.clone(), Some(authenticator)); let mut rng = thread_rng(); From df3a19b8f52282352eb1e6e576b7bcb34baaf9ce Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Fri, 25 Oct 2024 14:57:42 -0700 Subject: [PATCH 22/29] fix: lint --- bin/faucet/src/client.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 84e0cf5c..220606e9 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -25,10 +25,9 @@ use miden_objects::{ BlockHeader, Felt, Word, }; use miden_tx::{ - auth::BasicAuthenticator, - utils::Serializable, - DataStore, DataStoreError, LocalTransactionProver, ProvingOptions, TransactionExecutor, - TransactionInputs, TransactionProver, + auth::BasicAuthenticator, utils::Serializable, DataStore, DataStoreError, + LocalTransactionProver, ProvingOptions, TransactionExecutor, TransactionInputs, + TransactionProver, }; use rand::{rngs::StdRng, thread_rng, Rng}; use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; From 915edf272719974edb4f9b4070c956f004f5de86 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:42:17 +0500 Subject: [PATCH 23/29] docs: update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3733d508..e702e60c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ - Migrated faucet from actix-web to axum (#511). - Changed the `BlockWitness` to pass the inputs to the VM using only advice provider (#516). - [BREAKING] Improved store API errors (return "not found" instead of "internal error" status if requested account(s) not found) (#518). -- Support for faucet restarting without blockchain restarting (#517). +- [BREAKING] Changed faucet storage type in the genesis to public. Using faucet from the genesis for faucet web app. Added support for faucet restarting without blockchain restarting (#517). ## 0.5.1 (2024-09-12) From 4384a76744e5e59caaeb34acf831c4805178ca81 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:58:26 +0500 Subject: [PATCH 24/29] fix: compilation errors --- bin/faucet/src/client.rs | 32 +++++++++++++++----------------- bin/faucet/src/store.rs | 13 ++++++------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index fc28b42a..f15b8ec2 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -1,7 +1,4 @@ -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; +use std::{sync::Arc, time::Duration}; use anyhow::Context; use miden_lib::{notes::create_p2id_note, transaction::TransactionKernel}; @@ -68,15 +65,6 @@ impl FaucetClient { let id = faucet_account_data.account.id(); - let public_key = match &faucet_account_data.auth_secret_key { - AuthSecretKey::RpoFalcon512(secret) => secret.public_key(), - }; - - let authenticator = BasicAuthenticator::::new(&[( - public_key.into(), - faucet_account_data.auth_secret_key, - )]); - info!(target: COMPONENT, "Requesting account state from the node..."); let faucet_account = match request_account_state(&mut rpc_api, id).await { Ok(account) => { @@ -107,9 +95,18 @@ impl FaucetClient { faucet_account_data.account_seed, root_block_header, root_chain_mmr, - ); + )); + + let public_key = match &faucet_account_data.auth_secret_key { + AuthSecretKey::RpoFalcon512(secret) => secret.public_key(), + }; + + let authenticator = Arc::new(BasicAuthenticator::::new(&[( + public_key.into(), + faucet_account_data.auth_secret_key, + )])); - let executor = TransactionExecutor::new(data_store.clone(), Some(Rc::new(authenticator))); + let executor = TransactionExecutor::new(data_store.clone(), Some(authenticator)); let coin_seed: [u64; 4] = random(); let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); @@ -166,8 +163,9 @@ impl FaucetClient { let request = { let transaction_prover = LocalTransactionProver::new(ProvingOptions::default()); - let proven_transaction = - transaction_prover.prove(executed_tx.into()).context("Failed to prove transaction")?; + let proven_transaction = transaction_prover + .prove(executed_tx.into()) + .context("Failed to prove transaction")?; SubmitProvenTransactionRequest { transaction: proven_transaction.to_bytes(), diff --git a/bin/faucet/src/store.rs b/bin/faucet/src/store.rs index 38406997..02050765 100644 --- a/bin/faucet/src/store.rs +++ b/bin/faucet/src/store.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, RwLock}; +use std::sync::Mutex; use miden_objects::{ accounts::{Account, AccountId}, @@ -10,9 +10,8 @@ use miden_tx::{DataStore, DataStoreError}; use crate::errors::HandlerError; -#[derive(Clone)] pub struct FaucetDataStore { - faucet_account: Arc>, + faucet_account: Mutex, /// Optional initial seed used for faucet account creation. init_seed: Option, block_header: BlockHeader, @@ -30,7 +29,7 @@ impl FaucetDataStore { chain_mmr: ChainMmr, ) -> Self { Self { - faucet_account: Arc::new(RwLock::new(faucet_account)), + faucet_account: Mutex::new(faucet_account), init_seed, block_header, chain_mmr, @@ -39,12 +38,12 @@ impl FaucetDataStore { /// Returns the stored faucet account. pub fn faucet_account(&self) -> Account { - self.faucet_account.read().expect("Poisoned lock").clone() + self.faucet_account.lock().expect("Poisoned lock").clone() } /// Updates the stored faucet account with the new one. pub async fn update_faucet_state(&self, new_faucet_state: Account) -> Result<(), HandlerError> { - *self.faucet_account.write().expect("Poisoned lock") = new_faucet_state; + *self.faucet_account.lock().expect("Poisoned lock") = new_faucet_state; Ok(()) } @@ -57,7 +56,7 @@ impl DataStore for FaucetDataStore { _block_ref: u32, _notes: &[NoteId], ) -> Result { - let account = self.faucet_account.read().expect("Poisoned lock"); + let account = self.faucet_account.lock().expect("Poisoned lock"); if account_id != account.id() { return Err(DataStoreError::AccountNotFound(account_id)); } From ea5d59cd9a7c9c929f93b19033db94895c909e54 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:16:40 +0500 Subject: [PATCH 25/29] docs: add `TODO` for incorrect and unsafe `Send` implementation --- bin/faucet/src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index f15b8ec2..9ecdbd76 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -52,6 +52,7 @@ pub struct FaucetClient { rng: RpoRandomCoin, } +// TODO: Remove this once https://github.com/0xPolygonMiden/miden-base/issues/909 is resolved unsafe impl Send for FaucetClient {} impl FaucetClient { From f11111583986645ecd16427a4b4212967f6c24dd Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:49:07 +0500 Subject: [PATCH 26/29] refactor: small refactoring --- bin/faucet/src/client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 9ecdbd76..023e3515 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -102,12 +102,12 @@ impl FaucetClient { AuthSecretKey::RpoFalcon512(secret) => secret.public_key(), }; - let authenticator = Arc::new(BasicAuthenticator::::new(&[( + let authenticator = BasicAuthenticator::::new(&[( public_key.into(), faucet_account_data.auth_secret_key, - )])); + )]); - let executor = TransactionExecutor::new(data_store.clone(), Some(authenticator)); + let executor = TransactionExecutor::new(data_store.clone(), Some(Arc::new(authenticator))); let coin_seed: [u64; 4] = random(); let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); From 0a9f0684181fee85a1b25344c594eccf256214e5 Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:01:04 +0500 Subject: [PATCH 27/29] refactor: address review comments --- bin/faucet/src/client.rs | 6 +++++- bin/faucet/src/main.rs | 4 +++- bin/node/src/commands/genesis/mod.rs | 9 ++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/bin/faucet/src/client.rs b/bin/faucet/src/client.rs index 023e3515..ce76a37e 100644 --- a/bin/faucet/src/client.rs +++ b/bin/faucet/src/client.rs @@ -56,7 +56,11 @@ pub struct FaucetClient { unsafe impl Send for FaucetClient {} impl FaucetClient { - /// Creates a new faucet client. + /// Fetches the latest faucet account state from the node and creates a new faucet client. + /// + /// # Note + /// If the faucet account is not found on chain, it will be created on submission of the first + /// minting transaction. pub async fn new(config: &FaucetConfig) -> Result { let (mut rpc_api, root_block_header, root_chain_mmr) = initialize_faucet_client(config).await?; diff --git a/bin/faucet/src/main.rs b/bin/faucet/src/main.rs index c5ce2c87..a172440a 100644 --- a/bin/faucet/src/main.rs +++ b/bin/faucet/src/main.rs @@ -59,7 +59,7 @@ pub enum Command { config: PathBuf, }, - /// Create a new public faucet account and save to the file + /// Create a new public faucet account and save to the specified file CreateFaucetAccount { #[arg(short, long, value_name = "FILE", default_value = DEFAULT_FAUCET_ACCOUNT_PATH)] output_path: PathBuf, @@ -133,6 +133,8 @@ async fn main() -> anyhow::Result<()> { decimals, max_supply, } => { + println!("Generating new faucet account. This may take a few minutes..."); + let current_dir = std::env::current_dir().context("Failed to open current directory")?; diff --git a/bin/node/src/commands/genesis/mod.rs b/bin/node/src/commands/genesis/mod.rs index cae7d14d..8e9d7f7f 100644 --- a/bin/node/src/commands/genesis/mod.rs +++ b/bin/node/src/commands/genesis/mod.rs @@ -133,11 +133,10 @@ fn create_accounts( auth_scheme, )?; - let name = if faucet_count == 0 { - "faucet".to_string() - } else { - format!("faucet{faucet_count}") - }; + let name = format!( + "faucet{}", + (faucet_count > 0).then(|| faucet_count.to_string()).unwrap_or_default() + ); faucet_count += 1; (AccountData::new(account, Some(account_seed), auth_secret_key), name) From 112fcb6e71dcd6db63812a3543249740fb48e01f Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:01:23 +0500 Subject: [PATCH 28/29] docs: update `README.md` for faucet --- bin/faucet/README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/bin/faucet/README.md b/bin/faucet/README.md index 2d9fe8ed..901e0e6a 100644 --- a/bin/faucet/README.md +++ b/bin/faucet/README.md @@ -14,12 +14,23 @@ make docker-run-node make install-faucet-testing ``` -3. Create the default faucet configuration file: +3. [Optional] Create faucet account. Skip this step if you want to use an account from the genesis. This will generate authentication keypair and generate and write public faucet account data with its keypair into the file specified in `output-path`. This account will not be created on chain yet, this will happen on the first minting transaction: + +```bash +miden-faucet create-faucet-account \ + --output-path \ + --token-symbol POL \ + --decimals 9 \ + --max-supply 1000000000 +``` + + +4. Create the default faucet configuration file. Specify the path to the faucet account file created on the previous step in the `-f` flag (if you want to use an account from the genesis, specify the path to the `faucet.mac` file generated by the [make-genesis](../../README.md#setup) command of the Miden node): ```bash -miden-faucet init +miden-faucet init -f ``` -4. Start the faucet server: +5. Start the faucet server: ```bash miden-faucet start ``` From d0b8b5ca9e99b7ff2b04b7dcebd724d73fd8a20e Mon Sep 17 00:00:00 2001 From: polydez <155382956+polydez@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:49:07 +0500 Subject: [PATCH 29/29] docs: improve instructions and clarifications in faucet's `README.md` --- bin/faucet/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/bin/faucet/README.md b/bin/faucet/README.md index 901e0e6a..2d7b15e5 100644 --- a/bin/faucet/README.md +++ b/bin/faucet/README.md @@ -2,8 +2,12 @@ This crate contains a binary for running a Miden rollup faucet. -## Running the faucet -1. Run a local node, for example using the docker image. From the "miden-node" repo root run the following commands: +## Running the faucet in testing mode + +> [!TIP] +> Miden account generation uses a proof-of-work puzzle to prevent DoS attacks. These puzzles can be quite expensive, especially for test purposes. You can lower the difficulty of the puzzle by appending `--features testing` to the `cargo install ..` invocation. + +1. Run a local node with the "testing" feature, for example using the docker image. From the "miden-node" repo root run the following commands: ```bash make docker-build-node make docker-run-node @@ -14,7 +18,7 @@ make docker-run-node make install-faucet-testing ``` -3. [Optional] Create faucet account. Skip this step if you want to use an account from the genesis. This will generate authentication keypair and generate and write public faucet account data with its keypair into the file specified in `output-path`. This account will not be created on chain yet, this will happen on the first minting transaction: +3. [Optional] Create faucet account (skip this step if you want to use an account from the genesis). This will generate authentication keypair and generate and write public faucet account data with its keypair into the file specified in `output-path`: ```bash miden-faucet create-faucet-account \ @@ -23,7 +27,8 @@ miden-faucet create-faucet-account \ --decimals 9 \ --max-supply 1000000000 ``` - +> [!TIP] +> This account will not be created on chain yet, creation on chain will happen on the first minting transaction. 4. Create the default faucet configuration file. Specify the path to the faucet account file created on the previous step in the `-f` flag (if you want to use an account from the genesis, specify the path to the `faucet.mac` file generated by the [make-genesis](../../README.md#setup) command of the Miden node): ```bash