diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 724e4e20b..267365655 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,19 @@ jobs: - name: make - test run: make test + doc-tests: + name: doc-tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/next' }} + - name: Install rust + run: rustup update --no-self-update + - name: Run doc-tests + run: make test-docs + integration-tests: name: integration-tests runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 5504cd3e7..c49e84da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1570,7 +1570,7 @@ dependencies = [ [[package]] name = "miden-rpc-proto" version = "0.6.0" -source = "git+https://github.com/0xPolygonMiden/miden-node?branch=next#e8f83147ba0277a089bf07eb8443f6bd4526727b" +source = "git+https://github.com/0xPolygonMiden/miden-node?branch=next#fc35e175e7fdbbb65be1db0272284ef4ea802290" [[package]] name = "miden-stdlib" diff --git a/Makefile b/Makefile index 48b1dd8b8..a88e644eb 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,10 @@ test: ## Run tests test-deps: ## Install dependencies for tests CODEGEN=1 cargo install cargo-nextest +.PHONY: test-docs +test-docs: ## Run documentation tests + CODEGEN=1 cargo test --doc $(FEATURES_CLIENT) + # --- Integration testing ------------------------------------------------------------------------- .PHONY: integration-test diff --git a/crates/rust-client/README.md b/crates/rust-client/README.md index 04899313d..54bb06db3 100644 --- a/crates/rust-client/README.md +++ b/crates/rust-client/README.md @@ -12,14 +12,16 @@ miden-client = { version = "0.6" } ## Crate Features -- `concurrent`: used to enable concurrency during execution and proof generation. Disabled by default. -- `idxdb`: includes `WebStore`, an IdexedDB implementation of the `Store` trait. Disabled by default. -- `sqlite`: includes `SqliteStore`, a SQLite implementation of the `Store` trait. Disabled by default. -- `tonic`: includes `TonicRpcClient`, a Tonic client to communicate with Miden node. Disabled by default. -- `web-tonic`: includes `WebTonicRpcClient`, an Tonic client to communicate with the Miden node in the browser. Disabled by default. -- `testing`: useful feature that lowers PoW difficulty when enabled, meant to be used during development and not on production. Disabled by default. - -To compile with `no_std`, disable default features via `--no-default-features` flag. +| Features | Description | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `concurrent` | Used to enable concurrency during execution and proof generation. **Disabled by default.** | +| `idxdb` | Includes `WebStore`, an IndexedDB implementation of the `Store` trait. **Disabled by default.** | +| `sqlite` | Includes `SqliteStore`, a SQLite implementation of the `Store` trait. This relies on the standard library. **Disabled by default.** | +| `tonic` | Includes `TonicRpcClient`, a Tonic client to communicate with Miden node. This relies on the standard library. **Disabled by default.** | +| `web-tonic` | Includes `WebTonicRpcClient`, a Tonic client to communicate with the Miden node in the browser. **Disabled by default.** | +| `testing` | Useful feature that lowers PoW difficulty when enabled, meant to be used during development and not in production. **Disabled by default.** | + +Features `sqlite` and `idxdb` are mutually exclusive. ### Store and RpcClient implementations diff --git a/crates/rust-client/src/accounts.rs b/crates/rust-client/src/accounts.rs index 73e0fec0a..116064203 100644 --- a/crates/rust-client/src/accounts.rs +++ b/crates/rust-client/src/accounts.rs @@ -1,8 +1,68 @@ //! The `accounts` module provides types and client APIs for managing accounts within the Miden -//! rollup network . +//! rollup network. //! -//! Once accounts start being tracked by the client, their state will be -//! updated accordingly on every transaction, and validated against the rollup on every sync. +//! Once accounts start being tracked by the client, their state will be updated accordingly on +//! every transaction, and validated against the rollup on every sync. +//! +//! An account can store assets and define rules for manipulating them. +//! +//! # Overview +//! +//! This module exposes several key features: +//! +//! - **Account types:** Use the [`AccountBuilder`] to construct new accounts, specifying account +//! type, storage mode (public/private), and attaching necessary components (e.g., basic wallet or +//! fungible faucet). +//! +//! - **Account Tracking:** Accounts added via the client are persisted to the local store, where +//! their state (including nonce, balance, and metadata) is updated upon every synchronization +//! with the network. +//! +//! - **Data retrieval APIs:** The module also provides methods to fetch account-related data. +//! +//! # Example +//! +//! To add a new account to the client's store, you might use the [`Client::add_account`] method as +//! follows: +//! +//! ```rust +//! # use miden_client::accounts::{Account, AccountBuilder, AccountType, BasicWalletComponent}; +//! # use miden_objects::accounts::{AuthSecretKey, AccountStorageMode}; +//! # use miden_client::crypto::{FeltRng, SecretKey}; +//! # async fn add_new_account_example(client: &mut miden_client::Client) -> Result<(), miden_client::ClientError> { +//! let key_pair = SecretKey::with_rng(client.rng()); +//! # let random_seed = Default::default(); +//! +//! let (account, seed) = AccountBuilder::new(random_seed) +//! .account_type(AccountType::RegularAccountImmutableCode) +//! .storage_mode(AccountStorageMode::Private) +//! .with_component(BasicWalletComponent) +//! .build()?; +//! +//! // Add the account to the client. The account seed and authentication key are required for new accounts. +//! client.add_account(&account, Some(seed), &AuthSecretKey::RpoFalcon512(key_pair), false).await?; +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Account composition +//! +//! An account consists of the following components: +//! - Account ID, which uniquely identifies the account and also defines basic properties of the +//! account. +//! - Account vault, which stores assets owned by the account. +//! - Account storage, which is a key-value map (both keys and values are words) used to store +//! arbitrary user-defined data. +//! - Account code, which is a set of Miden VM programs defining the public interface of the +//! account. +//! - Account nonce, a value which is incremented whenever account state is updated. +//! +//! Out of the above components account ID is always immutable (once defined it can never be +//! changed). Other components may be mutated throughout the lifetime of the account. However, +//! account state can be changed only by invoking one of account interface methods. +//! +//! For more details on account management and synchronization, refer to the Miden client +//! documentation. use alloc::vec::Vec; diff --git a/crates/rust-client/src/lib.rs b/crates/rust-client/src/lib.rs index 3f4f82002..157d89acc 100644 --- a/crates/rust-client/src/lib.rs +++ b/crates/rust-client/src/lib.rs @@ -1,3 +1,102 @@ +//! A no_std-compatible client library for interacting with the Miden rollup network. +//! +//! This crate provides a lightweight client that handles connections to Miden node, manages +//! accounts and their state, and facilitates executing, proving, and submitting transactions. +//! +//! For a protocol-level overview and guides for getting started, please visit the official +//! [Polygon Miden docs](https://docs.polygon.technology/miden/). +//! +//! ## Overview +//! +//! The library is organized into several key modules: +//! +//! - **Accounts:** Provides types, builders, and client APIs for managing accounts. Once accounts +//! are tracked by the client, their state is updated with every transaction and validated during +//! each sync. +//! +//! - **Notes:** Contains types and utilities for working with notes in the Miden client. +//! +//! - **RPC:** Facilitates communication with Miden node, exposing RPC methods for syncing state, +//! fetching block headers, and submitting transactions. +//! +//! - **Store:** Defines and implements the persistence layer for accounts, transactions, notes, and +//! other entities. +//! +//! - **Sync:** Provides functionality to synchronize the local state with the current state on the +//! Miden network. +//! +//! - **transactions:** Offers capabilities to build, execute, prove, and submit transactions. +//! +//! Additionally, the crate re-exports several utility modules: +//! +//! - **Assets:** Types and utilities for working with assets. +//! - **Auth:** Authentication-related types and functionalities. +//! - **Blocks:** Types for handling block headers. +//! - **Crypto:** Cryptographic types and utilities, including random number generators. +//! - **Utils:** Miscellaneous utilities for serialization and common operations. +//! +//! The library is designed to work in both `no_std` and `std` environments and is +//! configurable via Cargo features. +//! +//! ## Usage +//! +//! To use the Miden client library in your project, add it as a dependency in your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! miden-client = "0.6.0" +//! ``` +//! +//! ## Example +//! +//! Below is a brief example illustrating how to instantiate the client: +//! +//! ```rust +//! use std::sync::Arc; +//! +//! use miden_client::{ +//! crypto::RpoRandomCoin, +//! rpc::{Endpoint, TonicRpcClient}, +//! store::{sqlite_store::SqliteStore, Store, StoreAuthenticator}, +//! Client, Felt, +//! }; +//! use miden_objects::crypto::rand::FeltRng; +//! use rand::Rng; +//! +//! # pub async fn create_test_client() -> Result<(), Box> { +//! let client: Client = { +//! // Create the SQLite store from the client configuration. +//! let sqlite_store = SqliteStore::new("path/to/store".try_into()?).await?; +//! let store = Arc::new(sqlite_store); +//! +//! // Generate a random seed for the RpoRandomCoin. +//! let mut rng = rand::thread_rng(); +//! let coin_seed: [u64; 4] = rng.gen(); +//! +//! // Initialize the random coin using the generated seed. +//! let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); +//! +//! // Create a store authenticator with the store and random coin. +//! let authenticator = StoreAuthenticator::new_with_rng(store.clone(), rng); +//! +//! // Instantiate the client using a Tonic RPC client +//! let endpoint = Endpoint::new("https".into(), "localhost".into(), 57291); +//! Client::new( +//! Box::new(TonicRpcClient::new(endpoint, 10_000)), +//! rng, +//! store, +//! Arc::new(authenticator), +//! false, // Set to true for debug mode, if needed. +//! ) +//! }; +//! +//! # Ok(()) +//! # } +//! ``` +//! +//! For additional usage details, configuration options, and examples, consult the documentation for +//! each module. + #![no_std] #[macro_use] @@ -104,8 +203,7 @@ use tracing::info; /// Miden client is responsible for managing a set of accounts. Specifically, the client: /// - Keeps track of the current and historical states of a set of accounts and related objects such /// as notes and transactions. -/// - Connects to one or more Miden nodes to periodically sync with the current state of the -/// network. +/// - Connects to the Miden node to periodically sync with the current state of the network. /// - Executes, proves, and submits transactions to the network as directed by the user. pub struct Client { /// The client's store, which provides a way to write and read entities to provide persistence. diff --git a/crates/rust-client/src/notes/import.rs b/crates/rust-client/src/notes/import.rs index 167983d3a..1626344d0 100644 --- a/crates/rust-client/src/notes/import.rs +++ b/crates/rust-client/src/notes/import.rs @@ -1,3 +1,13 @@ +//! Provides note importing methods. +//! +//! This module allows users to import notes into the client's store. +//! Depending on the variant of [NoteFile] provided, the client will either fetch note details +//! from the network or create a new note record from supplied data. If a note already exists in +//! the store, it is updated with the new information. Additionally, the appropriate note tag +//! is tracked based on the imported note's metadata. +//! +//! For more specific information on how the process is performed, refer to the docs for +//! [Client::import_note()]. use alloc::string::ToString; use miden_objects::{ @@ -29,6 +39,8 @@ impl Client { /// - If the note file is a [NoteFile::NoteWithProof], the note is stored with the provided /// inclusion proof and metadata. The block header data is only fetched from the node if the /// note is committed in the past relative to the client. + /// + /// An error is returned if an attempt is made to overwrite a note that is currently processing. pub async fn import_note(&mut self, note_file: NoteFile) -> Result { let id = match ¬e_file { NoteFile::NoteId(id) => *id, @@ -77,7 +89,7 @@ impl Client { /// passed via `previous_note` so it can be updated. The note information is fetched from the /// node and stored in the client's store. /// - /// Errors: + /// # Errors: /// - If the note doesn't exist on the node. /// - If the note exists but is private. async fn import_note_record_by_id( diff --git a/crates/rust-client/src/notes/mod.rs b/crates/rust-client/src/notes/mod.rs index 5accc152e..0c06a5399 100644 --- a/crates/rust-client/src/notes/mod.rs +++ b/crates/rust-client/src/notes/mod.rs @@ -1,5 +1,61 @@ //! Contains the Client APIs related to notes. Notes can contain assets and scripts that are //! executed as part of transactions. +//! +//! This module enables the tracking, retrieval, and processing of notes. +//! It offers methods to query input and output notes from the store, check their consumability, +//! compile note scripts, and retrieve notes based on partial ID matching. +//! +//! ## Overview +//! +//! The module exposes APIs to: +//! +//! - Retrieve input notes and output notes. +//! - Determine the consumability of notes using the [NoteScreener]. +//! - Compile note scripts from source code with `compile_note_script`. +//! - Retrieve an input note by a prefix of its ID using the helper function +//! [get_input_note_with_id_prefix]. +//! +//! ## Example +//! +//! ```rust +//! use miden_client::{ +//! Client, +//! notes, +//! notes::{get_input_note_with_id_prefix, NoteScreener}, +//! store::NoteFilter, +//! crypto::FeltRng, +//! }; +//! use miden_objects::accounts::AccountId; +//! +//! # async fn example(client: &Client) -> Result<(), Box> { +//! // Retrieve all committed input notes +//! let input_notes = client.get_input_notes(NoteFilter::Committed).await?; +//! println!("Found {} committed input notes.", input_notes.len()); +//! +//! // Check consumability for a specific note +//! if let Some(note) = input_notes.first() { +//! let consumability = client.get_note_consumability(note.clone()).await?; +//! println!("Note consumability: {:?}", consumability); +//! } +//! +//! // Retrieve an input note by a partial ID match +//! let note_prefix = "0x70b7ec"; +//! match get_input_note_with_id_prefix(client, note_prefix).await { +//! Ok(note) => println!("Found note with matching prefix: {}", note.id().to_hex()), +//! Err(err) => println!("Error retrieving note: {:?}", err), +//! } +//! +//! // Compile the note script +//! let script_src = "begin push.9 push.12 add end"; +//! let note_script = client.compile_note_script(script_src)?; +//! println!("Compiled note script successfully."); +//! +//! # Ok(()) +//! # } +//! ``` +//! +//! For further details on the API and error handling, refer to the specific function and type +//! documentations in this module. use alloc::{collections::BTreeSet, string::ToString, vec::Vec}; @@ -121,7 +177,8 @@ impl Client { Ok(self.store.get_output_notes(NoteFilter::Unique(note_id)).await?.pop()) } - /// Compiles the provided program into a [NoteScript] + /// Compiles the provided program into a [NoteScript]. + /// The assembler uses the debug mode if the client is using debug mode. pub fn compile_note_script(&self, note_script: &str) -> Result { let assembler = TransactionKernel::assembler().with_debug_mode(self.in_debug_mode); NoteScript::compile(note_script, assembler).map_err(ClientError::NoteError) diff --git a/crates/rust-client/src/rpc/generated/nostd/requests.rs b/crates/rust-client/src/rpc/generated/nostd/requests.rs index 6cdccab33..a38b5f656 100644 --- a/crates/rust-client/src/rpc/generated/nostd/requests.rs +++ b/crates/rust-client/src/rpc/generated/nostd/requests.rs @@ -18,7 +18,7 @@ pub struct CheckNullifiersByPrefixRequest { #[prost(uint32, repeated, tag = "2")] pub nullifiers: ::prost::alloc::vec::Vec, } -/// Get a list of proofs for given nullifier hashes, each proof as a sparse Merkle Tree. +/// Returns a nullifier proof for each of the requested nullifiers. #[derive(Clone, PartialEq, ::prost::Message)] pub struct CheckNullifiersRequest { /// List of nullifiers to return proofs for. diff --git a/crates/rust-client/src/rpc/generated/nostd/rpc.rs b/crates/rust-client/src/rpc/generated/nostd/rpc.rs index ef7d29203..52f8ab263 100644 --- a/crates/rust-client/src/rpc/generated/nostd/rpc.rs +++ b/crates/rust-client/src/rpc/generated/nostd/rpc.rs @@ -79,7 +79,7 @@ pub mod api_client { self.inner = self.inner.max_encoding_message_size(limit); self } - /// Gets a list of proofs for given nullifier hashes, each proof as a sparse Merkle Tree. + /// Returns a nullifier proof for each of the requested nullifiers. pub async fn check_nullifiers( &mut self, request: impl tonic::IntoRequest< @@ -104,6 +104,8 @@ pub mod api_client { self.inner.unary(req, path, codec).await } /// Returns a list of nullifiers that match the specified prefixes and are recorded in the node. + /// + /// Note that only 16-bit prefixes are supported at this time. pub async fn check_nullifiers_by_prefix( &mut self, request: impl tonic::IntoRequest< @@ -208,7 +210,7 @@ pub mod api_client { .insert(GrpcMethod::new("rpc.Api", "GetAccountStateDelta")); self.inner.unary(req, path, codec).await } - /// Retrieves block data by given block number. + /// Returns raw block data for the specified block number. pub async fn get_block_by_number( &mut self, request: impl tonic::IntoRequest< @@ -309,10 +311,15 @@ pub mod api_client { .insert(GrpcMethod::new("rpc.Api", "SubmitProvenTransaction")); self.inner.unary(req, path, codec).await } - /// Note synchronization request. + /// Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in. + /// + /// Client specifies the `note_tags` they are interested in, and the block height from which to search for new for + /// matching notes for. The request will then return the next block containing any note matching the provided tags. + /// + /// The response includes each note's metadata and inclusion proof. /// - /// Specifies note tags that client is interested in. The server will return the first block which - /// contains a note matching `note_tags` or the chain tip. + /// A basic note sync can be implemented by repeatedly requesting the previous response's block until reaching the + /// tip of the chain. pub async fn sync_notes( &mut self, request: impl tonic::IntoRequest, diff --git a/crates/rust-client/src/rpc/generated/std/requests.rs b/crates/rust-client/src/rpc/generated/std/requests.rs index 6cdccab33..a38b5f656 100644 --- a/crates/rust-client/src/rpc/generated/std/requests.rs +++ b/crates/rust-client/src/rpc/generated/std/requests.rs @@ -18,7 +18,7 @@ pub struct CheckNullifiersByPrefixRequest { #[prost(uint32, repeated, tag = "2")] pub nullifiers: ::prost::alloc::vec::Vec, } -/// Get a list of proofs for given nullifier hashes, each proof as a sparse Merkle Tree. +/// Returns a nullifier proof for each of the requested nullifiers. #[derive(Clone, PartialEq, ::prost::Message)] pub struct CheckNullifiersRequest { /// List of nullifiers to return proofs for. diff --git a/crates/rust-client/src/rpc/generated/std/rpc.rs b/crates/rust-client/src/rpc/generated/std/rpc.rs index 5067a8cb8..60f359f30 100644 --- a/crates/rust-client/src/rpc/generated/std/rpc.rs +++ b/crates/rust-client/src/rpc/generated/std/rpc.rs @@ -90,7 +90,7 @@ pub mod api_client { self.inner = self.inner.max_encoding_message_size(limit); self } - /// Gets a list of proofs for given nullifier hashes, each proof as a sparse Merkle Tree. + /// Returns a nullifier proof for each of the requested nullifiers. pub async fn check_nullifiers( &mut self, request: impl tonic::IntoRequest< @@ -115,6 +115,8 @@ pub mod api_client { self.inner.unary(req, path, codec).await } /// Returns a list of nullifiers that match the specified prefixes and are recorded in the node. + /// + /// Note that only 16-bit prefixes are supported at this time. pub async fn check_nullifiers_by_prefix( &mut self, request: impl tonic::IntoRequest< @@ -219,7 +221,7 @@ pub mod api_client { .insert(GrpcMethod::new("rpc.Api", "GetAccountStateDelta")); self.inner.unary(req, path, codec).await } - /// Retrieves block data by given block number. + /// Returns raw block data for the specified block number. pub async fn get_block_by_number( &mut self, request: impl tonic::IntoRequest< @@ -320,10 +322,15 @@ pub mod api_client { .insert(GrpcMethod::new("rpc.Api", "SubmitProvenTransaction")); self.inner.unary(req, path, codec).await } - /// Note synchronization request. + /// Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in. + /// + /// Client specifies the `note_tags` they are interested in, and the block height from which to search for new for + /// matching notes for. The request will then return the next block containing any note matching the provided tags. + /// + /// The response includes each note's metadata and inclusion proof. /// - /// Specifies note tags that client is interested in. The server will return the first block which - /// contains a note matching `note_tags` or the chain tip. + /// A basic note sync can be implemented by repeatedly requesting the previous response's block until reaching the + /// tip of the chain. pub async fn sync_notes( &mut self, request: impl tonic::IntoRequest, diff --git a/crates/rust-client/src/rpc/mod.rs b/crates/rust-client/src/rpc/mod.rs index e24f96f85..0fcd5660c 100644 --- a/crates/rust-client/src/rpc/mod.rs +++ b/crates/rust-client/src/rpc/mod.rs @@ -1,6 +1,42 @@ -//! Provides an interface for the client to communicate with Miden nodes using -//! Remote Procedure Calls (RPC). It facilitates syncing with the network and submitting -//! transactions. +//! Provides an interface for the client to communicate with Miden node using +//! Remote Procedure Calls (RPC). +//! This module defines the [NodeRpcClient] trait which abstracts calls to the RPC protocol used to: +//! +//! - Submit proven transactions. +//! - Retrieve block headers (optionally with MMR proofs). +//! - Sync state updates (including notes, nullifiers, and account updates). +//! - Fetch details for specific notes and accounts. +//! +//! In addition, the module provides implementations for different environments (e.g. tonic-based or +//! web-based) via feature flags ( `tonic` and `web-tonic`). +//! +//! ## Example +//! +//! ```no_run +//! # use miden_client::rpc::{NodeRpcClient, TonicRpcClient}; +//! # use miden_objects::block::BlockNumber; +//! # use miden_client::rpc::Endpoint; +//! #[tokio::main] +//! # async fn main() -> Result<(), Box> { +//! // Create a Tonic RPC client instance (assumes default endpoint configuration). +//! let endpoint = Endpoint::new("https".into(), "localhost".into(), 57291); +//! let mut rpc_client = TonicRpcClient::new(endpoint, 1000); +//! +//! // Fetch the latest block header (by passing None). +//! let (block_header, mmr_proof) = rpc_client.get_block_header_by_number(None, true).await?; +//! +//! println!("Latest block number: {}", block_header.block_num()); +//! if let Some(proof) = mmr_proof { +//! println!("MMR proof received accordingly"); +//! } +//! +//! # Ok(()) +//! # } +//! ``` +//! The client also makes use of this component in order to communicate with the node. +//! +//! For further details and examples, see the documentation for the individual methods in the +//! [NodeRpcClient] trait. use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec}; use core::fmt; diff --git a/crates/rust-client/src/store/mod.rs b/crates/rust-client/src/store/mod.rs index b10144ad4..8f2311a33 100644 --- a/crates/rust-client/src/store/mod.rs +++ b/crates/rust-client/src/store/mod.rs @@ -1,5 +1,24 @@ //! Defines the storage interfaces used by the Miden client. It provides mechanisms for persisting -//! and retrieving data, such as account states, transaction history, and block headers. +//! and retrieving data, such as account states, transaction history, block headers, notes, and MMR +//! authentication nodes. +//! +//! ## Overview +//! +//! The storage module is central to the Miden client’s persistence layer. It defines the +//! [`Store`] trait which abstracts over any concrete storage implementation. The trait exposes +//! methods to (among others): +//! +//! - Retrieve and update transactions, notes, and accounts. +//! - Store and query block headers along with MMR peaks and authentication nodes. +//! - Manage note tags for synchronizing with the node. +//! +//! These are all used by the Midne client to provide transaction execution in the correct contexts. +//! +//! In addition to the main [`Store`] trait, the module provides types for filtering queries, such +//! as [`TransactionFilter`] and [`NoteFilter`], to narrow down the set of returned transactions or +//! notes. For more advanced usage, see the documentation of individual methods in the [`Store`] +//! trait. + use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, diff --git a/crates/rust-client/src/store/note_record/input_note_record/mod.rs b/crates/rust-client/src/store/note_record/input_note_record/mod.rs index 779a58bc4..627acdef6 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/mod.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/mod.rs @@ -30,6 +30,9 @@ pub use states::{ /// as input for transactions. /// It is also possible to convert [Note] and [InputNote] into [InputNoteRecord] (we fill the /// `metadata` and `inclusion_proof` fields if possible). +/// +/// Notes can also be consumed as unauthenticated notes, where the note's existence is verified on +/// the network. #[derive(Clone, Debug, PartialEq)] pub struct InputNoteRecord { /// Details of a note consisting of assets, script, inputs, and a serial number. diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/committed.rs b/crates/rust-client/src/store/note_record/input_note_record/states/committed.rs index 4979ff174..77d9cfec7 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/committed.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/committed.rs @@ -13,6 +13,7 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::Committed] state. #[derive(Clone, Debug, PartialEq)] pub struct CommittedNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_authenticated_local.rs b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_authenticated_local.rs index b11512a98..0ac1cfeb2 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_authenticated_local.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_authenticated_local.rs @@ -10,6 +10,7 @@ use miden_objects::{ use super::{InputNoteState, NoteStateHandler, NoteSubmissionData}; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::ConsumedAuthenticatedLocal] state. #[derive(Clone, Debug, PartialEq)] pub struct ConsumedAuthenticatedLocalNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_external.rs b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_external.rs index 067c79ac5..994808bbd 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_external.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_external.rs @@ -9,6 +9,7 @@ use miden_objects::{ use super::{InputNoteState, NoteStateHandler}; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::ConsumedExternal] state. #[derive(Clone, Debug, PartialEq)] pub struct ConsumedExternalNoteState { /// Block height at which the note was nullified. diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_unauthenticated_local.rs b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_unauthenticated_local.rs index 9aefae8c1..f1866a6eb 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/consumed_unauthenticated_local.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/consumed_unauthenticated_local.rs @@ -9,6 +9,8 @@ use miden_objects::{ use super::{InputNoteState, NoteStateHandler, NoteSubmissionData}; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::ConsumedUnauthenticatedLocal] state. + #[derive(Clone, Debug, PartialEq)] pub struct ConsumedUnauthenticatedLocalNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/expected.rs b/crates/rust-client/src/store/note_record/input_note_record/states/expected.rs index d46a954be..e16219cd0 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/expected.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/expected.rs @@ -13,6 +13,7 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::Expected] state. #[derive(Clone, Debug, PartialEq)] pub struct ExpectedNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/invalid.rs b/crates/rust-client/src/store/note_record/input_note_record/states/invalid.rs index 1d5b861a1..6fa068154 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/invalid.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/invalid.rs @@ -13,12 +13,13 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::Invalid] state. #[derive(Clone, Debug, PartialEq)] pub struct InvalidNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional /// information. pub metadata: NoteMetadata, - /// Inclusion proof for the note inside the chain block. + /// Inclusion proof for the note inside the chain block, verified to be invalid. pub invalid_inclusion_proof: NoteInclusionProof, /// Root of the note tree inside the block that invalidates the note inclusion proof. pub block_note_root: Digest, diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/mod.rs b/crates/rust-client/src/store/note_record/input_note_record/states/mod.rs index ae5db78b9..e0798f791 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/mod.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/mod.rs @@ -38,23 +38,24 @@ use super::NoteRecordError; /// The possible states of a tracked note. pub enum InputNoteState { - /// Tracked by the client but without a chain inclusion proof. + /// Tracked by the client but without a network inclusion proof. Expected(ExpectedNoteState), - /// With inclusion proof but not yet verified. + /// The store holds the note's inclusion proof, but it was not yet verified. Unverified(UnverifiedNoteState), - /// With verified inclusion proof. + /// The store holds the note's inclusion proof, which was verified. Committed(CommittedNoteState), - /// With invalid inclusion proof. + /// The store holds the note's inclusion proof, which is invalid. Invalid(InvalidNoteState), - /// Authenticated note being consumed locally by the client, awaiting chain confirmation. + /// Authenticated note being consumed locally by the client, awaiting network confirmation. ProcessingAuthenticated(ProcessingAuthenticatedNoteState), - /// Unauthenticated note being consumed locally by the client, awaiting chain confirmation. + /// Unauthenticated note being consumed locally by the client, awaiting network confirmation. ProcessingUnauthenticated(ProcessingUnauthenticatedNoteState), - /// Authenticated note consumed locally by the client and confirmed by the chain. + /// Authenticated note consumed locally by the client and confirmed by the network. ConsumedAuthenticatedLocal(ConsumedAuthenticatedLocalNoteState), - /// Unauthenticated note consumed locally by the client and confirmed by the chain. + /// Unauthenticated note consumed locally by the client and confirmed by the network. ConsumedUnauthenticatedLocal(ConsumedUnauthenticatedLocalNoteState), - /// Note consumed in chain by an external account (e.g. an account not tracked by the client). + /// Note consumed in the network by an external account (e.g. an account not tracked by the + /// client). ConsumedExternal(ConsumedExternalNoteState), } diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/processing_authenticated.rs b/crates/rust-client/src/store/note_record/input_note_record/states/processing_authenticated.rs index 6611e445a..5798a1ef5 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/processing_authenticated.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/processing_authenticated.rs @@ -14,6 +14,7 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::ProcessingAuthenticated] state. #[derive(Clone, Debug, PartialEq)] pub struct ProcessingAuthenticatedNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/processing_unauthenticated.rs b/crates/rust-client/src/store/note_record/input_note_record/states/processing_unauthenticated.rs index 22ded7ca3..fba7ff4fe 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/processing_unauthenticated.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/processing_unauthenticated.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::ProcessingUnauthenticated] state. #[derive(Clone, Debug, PartialEq)] pub struct ProcessingUnauthenticatedNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/input_note_record/states/unverified.rs b/crates/rust-client/src/store/note_record/input_note_record/states/unverified.rs index 543a2a614..fde610263 100644 --- a/crates/rust-client/src/store/note_record/input_note_record/states/unverified.rs +++ b/crates/rust-client/src/store/note_record/input_note_record/states/unverified.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::store::NoteRecordError; +/// Information related to notes in the [InputNoteState::Unverified] state. #[derive(Clone, Debug, PartialEq)] pub struct UnverifiedNoteState { /// Metadata associated with the note, including sender, note type, tag and other additional diff --git a/crates/rust-client/src/store/note_record/mod.rs b/crates/rust-client/src/store/note_record/mod.rs index 81efce71a..9a6026eb1 100644 --- a/crates/rust-client/src/store/note_record/mod.rs +++ b/crates/rust-client/src/store/note_record/mod.rs @@ -2,16 +2,11 @@ //! that are available to be consumed ([InputNoteRecord]) and notes that have been produced as a //! result of executing a transaction ([OutputNoteRecord]). //! -//! # Features +//! Both structs are similar in terms of the data they carry, but are differentiated semantically +//! as they are involved in very different flows. As such, known states are modeled differently for +//! the two structures, with [InputNoteRecord] having states described by the [InputNoteState] enum. //! -//! ## Serialization / Deserialization -//! -//! We provide serialization and deserialization support via [Serializable] and [Deserializable] -//! traits implementations. -//! -//! ## Type conversion -//! -//! We also facilitate converting from/into [InputNote](miden_objects::transaction::InputNote) / +//! Conversions are provided from/into [InputNote](miden_objects::transaction::InputNote) / //! [Note](miden_objects::notes::Note), although this is not always possible. Check both //! [InputNoteRecord]'s and [OutputNoteRecord]'s documentation for more details about this. @@ -25,6 +20,8 @@ mod output_note_record; pub use input_note_record::{InputNoteRecord, InputNoteState}; pub use output_note_record::{NoteExportType, OutputNoteRecord, OutputNoteState}; + +/// Contains structures that model all states in which an input note can be. pub mod input_note_states { pub use super::input_note_record::{ CommittedNoteState, ConsumedAuthenticatedLocalNoteState, ExpectedNoteState, InputNoteState, diff --git a/crates/rust-client/src/store/sqlite_store/mod.rs b/crates/rust-client/src/store/sqlite_store/mod.rs index ce1708b55..1920b598d 100644 --- a/crates/rust-client/src/store/sqlite_store/mod.rs +++ b/crates/rust-client/src/store/sqlite_store/mod.rs @@ -1,3 +1,7 @@ +//! This module provides an SQLite-backed implementation of the [Store] trait (enabled with the +//! `sqlite` feature). It enables persistence of accounts, transactions, notes, block headers, and +//! MMR nodes using an SQLite database. + use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, diff --git a/crates/rust-client/src/store/web_store/mod.rs b/crates/rust-client/src/store/web_store/mod.rs index 13a5578c7..4f1fcfbd7 100644 --- a/crates/rust-client/src/store/web_store/mod.rs +++ b/crates/rust-client/src/store/web_store/mod.rs @@ -1,3 +1,12 @@ +//! Provides an IndexedDB-backed implementation of the [Store] trait for web environments. +//! +//! This module enables persistence of client data (accounts, transactions, notes, block headers, +//! etc.) when running in a browser. It uses wasm-bindgen to interface with JavaScript and +//! IndexedDB, allowing the Miden client to store and retrieve data asynchronously. +//! +//! **Note:** This implementation is only available when targeting WebAssembly with the `web_store` +//! feature enabled. + use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; use miden_objects::{ diff --git a/crates/rust-client/src/sync/mod.rs b/crates/rust-client/src/sync/mod.rs index 3a126353e..e0a2a7e98 100644 --- a/crates/rust-client/src/sync/mod.rs +++ b/crates/rust-client/src/sync/mod.rs @@ -1,5 +1,58 @@ //! Provides the client APIs for synchronizing the client's local state with the Miden //! rollup network. It ensures that the client maintains a valid, up-to-date view of the chain. +//! +//! ## Overview +//! +//! This module handles the synchronization process between the local client and the Miden network. +//! The sync operation involves: +//! +//! - Querying the Miden node for state updates using tracked account IDs, note tags, and nullifier +//! prefixes. +//! - Processing the received data to update note inclusion proofs, reconcile note state (new, +//! committed, or consumed), and update account states. +//! - Incorporating new block headers and updating the local Merkle Mountain Range (MMR) with new +//! peaks and authentication nodes. +//! - Aggregating transaction updates to determine which transactions have been committed or +//! discarded. +//! +//! The result of the synchronization process is captured in a [`SyncSummary`], which provides +//! a summary of the new block number along with lists of received, committed, and consumed note +//! IDs, updated account IDs, locked accounts, and committed transaction IDs. +//! +//! Once the data is requested and retrieved, updates are persisted in the client's store. +//! +//! ## Examples +//! +//! The following example shows how to initiate a state sync and handle the resulting summary: +//! +//! ```rust +//! # use miden_client::sync::SyncSummary; +//! # use miden_client::{Client, ClientError}; +//! # use miden_objects::{block::BlockHeader, Felt, Word, StarkField}; +//! # use miden_objects::crypto::rand::FeltRng; +//! # async fn run_sync(client: &mut Client) -> Result<(), ClientError> { +//! // Attempt to synchronize the client's state with the Miden network. +//! // The requested data is based on the client's state: it gets updates for accounts, relevant notes, etc. +//! // For more information on the data that getes requested, see the doc comments for `sync_state()` +//! let sync_summary: SyncSummary = client.sync_state().await?; +//! +//! println!("Synced up to block number: {}", sync_summary.block_num); +//! println!("Received notes: {}", sync_summary.received_notes.len()); +//! println!("Committed notes: {}", sync_summary.committed_notes.len()); +//! println!("Consumed notes: {}", sync_summary.consumed_notes.len()); +//! println!("Updated accounts: {}", sync_summary.updated_accounts.len()); +//! println!("Locked accounts: {}", sync_summary.locked_accounts.len()); +//! println!("Committed transactions: {}", sync_summary.committed_transactions.len()); +//! +//! Ok(()) +//! # } +//! ``` +//! +//! The `sync_state` method loops internally until the client is fully synced to the network tip. +//! +//! For more advanced usage, refer to the individual functions (such as +//! `committed_note_updates` and `consumed_note_updates`) to understand how the sync data is +//! processed and applied to the local store. use alloc::{collections::BTreeMap, vec::Vec}; use core::cmp::max; diff --git a/crates/rust-client/src/transactions/mod.rs b/crates/rust-client/src/transactions/mod.rs index 0362f964a..0b53a1957 100644 --- a/crates/rust-client/src/transactions/mod.rs +++ b/crates/rust-client/src/transactions/mod.rs @@ -1,5 +1,58 @@ -//! Provides APIs for transaction creation, execution, and proving. -//! It also handles proof submission to the network. +//! Provides APIs for transaction creation, execution, and proving. +//! Additionally handles proof submission to the network. +//! +//! ## Overview +//! +//! This module enables clients to: +//! +//! - Build transaction requests using the [TransactionRequestBuilder]. +//! - [TransactionRequestBuilder] contains simple builders for standard transaction types, such as +//! `p2id` (pay-to-id) +//! - Execute transactions via the local transaction executor and generate a [TransactionResult] +//! that includes execution details and relevant notes for state tracking. +//! - Prove transactions (locally or remotely) using a [TransactionProver] and submit the proven +//! transactions to the network. +//! - Track and update the state of transactions, including their status (e.g., `Pending``, +//! `Committed`, or `Discarded``). +//! +//! ## Example +//! +//! The following example demonstrates how to create and submit a transaction: +//! +//! ```rust +//! use miden_client::{Client,transactions::{TransactionRequestBuilder, PaymentTransactionData, TransactionResult}}; +//! use miden_objects::accounts::AccountId; +//! use miden_objects::assets::FungibleAsset; +//! use miden_objects::notes::NoteType; +//! use miden_client::crypto::FeltRng; +//! +//! /// Executes, proves and submits a P2ID transaction. +//! /// This transaction is executed by `sender_id`, and creates an output note containing 100 tokens of `faucet_id`'s fungible asset. +//! async fn create_and_submit_transaction(client: &mut Client, sender_id: AccountId, target_id: AccountId, faucet_id: AccountId) -> Result<(), Box> { +//! // Create an asset representing the amount to be transferred. +//! let asset = FungibleAsset::new(faucet_id, 100)?; +//! +//! // Build a transaction request for a pay-to-id transaction. +//! let tx_request = TransactionRequestBuilder::pay_to_id( +//! PaymentTransactionData::new(vec![asset.into()], sender_id, target_id), +//! None, // No recall height provided +//! NoteType::Private, +//! client.rng(), +//! )? +//! .build(); +//! +//! // Execute the transaction; this returns a TransactionResult. +//! let tx_result: TransactionResult = client.new_transaction(sender_id, tx_request).await?; +//! +//! // Prove and submit the transaction, persisting its details to the local store. +//! client.submit_transaction(tx_result).await?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! For more detailed information about each function and error type, refer to the specific API +//! documentation. use alloc::{ collections::{BTreeMap, BTreeSet}, diff --git a/crates/web-client/Cargo.toml b/crates/web-client/Cargo.toml index cca2ae114..fcab5de86 100644 --- a/crates/web-client/Cargo.toml +++ b/crates/web-client/Cargo.toml @@ -23,7 +23,6 @@ miden-client = { version = "0.6", path = "../rust-client", default-features = fa miden-lib = { workspace = true } miden-objects = { workspace = true } miden-proving-service-client = { git = "https://github.com/0xPolygonMiden/miden-base", branch = "next", default-features = false, features = ["tx-prover"] } -miden-tx = { workspace = true } rand = { workspace = true } serde = { workspace = true } serde-wasm-bindgen = { version = "0.6" } diff --git a/docs/install-and-run.md b/docs/install-and-run.md index 4a072e717..8095715a3 100644 --- a/docs/install-and-run.md +++ b/docs/install-and-run.md @@ -16,7 +16,7 @@ Run the following command to install the miden-client: cargo install miden-cli --features concurrent ``` -This installs the `miden` binary (at `~/.cargo/bin/miden`) with the [`concurrent`](#concurrent-feature) features. +This installs the `miden` binary (at `~/.cargo/bin/miden`) with the [`concurrent`](#concurrent-feature) feature. ### `Concurrent` feature diff --git a/docs/library.md b/docs/library.md index 18094528d..0ec2bb673 100644 --- a/docs/library.md +++ b/docs/library.md @@ -12,39 +12,40 @@ miden-client = { version = "0.6" } ### Features -The Miden client library supports the [`testing`](https://github.com/0xPolygonMiden/miden-client/blob/main/docs/install-and-run.md#testing-feature) and [`concurrent`](https://github.com/0xPolygonMiden/miden-client/blob/main/docs/install-and-run.md#concurrent-feature) features which are both recommended for developing applications with the client. To use them, add the following to your project's `Cargo.toml`: +The Miden client library supports the `testing` and [`concurrent`](https://github.com/0xPolygonMiden/miden-client/blob/main/docs/install-and-run.md#concurrent-feature) features which are both recommended for developing applications with the client. To use them, add the following to your project's `Cargo.toml`: ```toml miden-client = { version = "0.6", features = ["testing", "concurrent"] } ``` +The library also supports several other features. Please refer to the crate's documentation to learn more. + ## Client instantiation Spin up a client using the following Rust code and supplying a store and RPC endpoint. -The current supported store is the `SqliteDataStore`, which is a SQLite implementation of the `Store` trait. - ```rust -let client: Client = { - - let store = SqliteStore::new((&client_config).into()).await.map_err(ClientError::StoreError)?; - - let mut rng = rand::thread_rng(); - let coin_seed: [u64; 4] = rng.gen(); - - let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); - let authenticator = StoreAuthenticator::new_with_rng(store.clone(), rng); - let tx_prover = LocalTransactionProver::new(ProvingOptions::default()); - - let client = Client::new( - Box::new(TonicRpcClient::new(&client_config.rpc)), - rng, - Arc::new(store), - Arc::new(authenticator), - Arc::new(tx_prover), - false, // set to true if you want a client with debug mode - ) -}; + let client: Client = { + // Create the SQLite store from the client configuration. + let sqlite_store = SqliteStore::new("path/to/store".try_into()?).await?; + let store = Arc::new(sqlite_store); + // Generate a random seed for the RpoRandomCoin. + let mut rng = rand::thread_rng(); + let coin_seed: [u64; 4] = rng.gen(); + // Initialize the random coin using the generated seed. + let rng = RpoRandomCoin::new(coin_seed.map(Felt::new)); + // Create a store authenticator with the store and random coin. + let authenticator = StoreAuthenticator::new_with_rng(store.clone(), rng); + // Instantiate the client using a Tonic RPC client + let endpoint = Endpoint::new("https".into(), "localhost".into(), 57291); + Client::new( + Box::new(TonicRpcClient::new(endpoint, 10_000)), + rng, + store, + Arc::new(authenticator), + false, // Set to true for debug mode, if needed. + ) + }; ``` ## Create local account