miden_client/
lib.rs

1//! A no_std-compatible client library for interacting with the Miden network.
2//!
3//! This crate provides a lightweight client that handles connections to the Miden node, manages
4//! accounts and their state, and facilitates executing, proving, and submitting transactions.
5//!
6//! For a protocol-level overview and guides for getting started, please visit the official
7//! [Polygon Miden docs](https://0xpolygonmiden.github.io/miden-docs/).
8//!
9//! ## Overview
10//!
11//! The library is organized into several key modules:
12//!
13//! - **Accounts:** Provides types for managing accounts. Once accounts are tracked by the client,
14//!   their state is updated with every transaction and validated during each sync.
15//!
16//! - **Notes:** Contains types and utilities for working with notes in the Miden client.
17//!
18//! - **RPC:** Facilitates communication with Miden node, exposing RPC methods for syncing state,
19//!   fetching block headers, and submitting transactions.
20//!
21//! - **Store:** Defines and implements the persistence layer for accounts, transactions, notes, and
22//!   other entities.
23//!
24//! - **Sync:** Provides functionality to synchronize the local state with the current state on the
25//!   Miden network.
26//!
27//! - **Transactions:** Offers capabilities to build, execute, prove, and submit transactions.
28//!
29//! Additionally, the crate re-exports several utility modules:
30//!
31//! - **Assets:** Types and utilities for working with assets.
32//! - **Auth:** Authentication-related types and functionalities.
33//! - **Blocks:** Types for handling block headers.
34//! - **Crypto:** Cryptographic types and utilities, including random number generators.
35//! - **Utils:** Miscellaneous utilities for serialization and common operations.
36//!
37//! The library is designed to work in both `no_std` and `std` environments and is
38//! configurable via Cargo features.
39//!
40//! ## Usage
41//!
42//! To use the Miden client library in your project, add it as a dependency in your `Cargo.toml`:
43//!
44//! ```toml
45//! [dependencies]
46//! miden-client = "0.8"
47//! ```
48//!
49//! ## Example
50//!
51//! Below is a brief example illustrating how to instantiate the client:
52//!
53//! ```rust
54//! use std::sync::Arc;
55//!
56//! use miden_client::{
57//!     Client, Felt,
58//!     crypto::RpoRandomCoin,
59//!     keystore::FilesystemKeyStore,
60//!     rpc::{Endpoint, TonicRpcClient},
61//!     store::{Store, sqlite_store::SqliteStore},
62//! };
63//! use miden_objects::crypto::rand::FeltRng;
64//! use rand::{Rng, rngs::StdRng};
65//!
66//! # pub async fn create_test_client() -> Result<(), Box<dyn std::error::Error>> {
67//! // Create the SQLite store from the client configuration.
68//! let sqlite_store = SqliteStore::new("path/to/store".try_into()?).await?;
69//! let store = Arc::new(sqlite_store);
70//!
71//! // Generate a random seed for the RpoRandomCoin.
72//! let mut rng = rand::rng();
73//! let coin_seed: [u64; 4] = rng.random();
74//!
75//! // Initialize the random coin using the generated seed.
76//! let rng = RpoRandomCoin::new(coin_seed.map(Felt::new));
77//! let keystore = FilesystemKeyStore::new("path/to/keys/directory".try_into()?)?;
78//!
79//! // Instantiate the client using a Tonic RPC client
80//! let endpoint = Endpoint::new("https".into(), "localhost".into(), Some(57291));
81//! let client: Client = Client::new(
82//!     Arc::new(TonicRpcClient::new(&endpoint, 10_000)),
83//!     Box::new(rng),
84//!     store,
85//!     Arc::new(keystore),
86//!     false, // Set to true for debug mode, if needed.
87//! );
88//!
89//! # Ok(())
90//! # }
91//! ```
92//!
93//! For additional usage details, configuration options, and examples, consult the documentation for
94//! each module.
95
96#![no_std]
97
98#[macro_use]
99extern crate alloc;
100use alloc::boxed::Box;
101
102#[cfg(feature = "std")]
103extern crate std;
104
105pub mod account;
106pub mod keystore;
107pub mod note;
108pub mod rpc;
109pub mod store;
110pub mod sync;
111pub mod transaction;
112
113#[cfg(feature = "std")]
114pub mod builder;
115
116#[cfg(test)]
117pub mod mock;
118
119#[cfg(test)]
120pub mod tests;
121
122mod errors;
123
124// RE-EXPORTS
125// ================================================================================================
126
127/// Provides types and utilities for working with assets within the Miden network.
128pub mod asset {
129    pub use miden_objects::{
130        account::delta::{
131            AccountVaultDelta, FungibleAssetDelta, NonFungibleAssetDelta, NonFungibleDeltaAction,
132        },
133        asset::{Asset, AssetVault, FungibleAsset, NonFungibleAsset, TokenSymbol},
134    };
135}
136
137/// Provides authentication-related types and functionalities for the Miden
138/// network.
139pub mod auth {
140    pub use miden_lib::AuthScheme;
141    pub use miden_objects::account::AuthSecretKey;
142    pub use miden_tx::auth::{BasicAuthenticator, TransactionAuthenticator};
143}
144
145/// Provides types for working with blocks within the Miden network.
146pub mod block {
147    pub use miden_objects::block::BlockHeader;
148}
149
150/// Provides cryptographic types and utilities used within the Miden rollup
151/// network. It re-exports commonly used types and random number generators like `FeltRng` from
152/// the `miden_objects` crate.
153pub mod crypto {
154    pub use miden_objects::{
155        Digest,
156        crypto::{
157            dsa::rpo_falcon512::SecretKey,
158            merkle::{
159                InOrderIndex, LeafIndex, MerklePath, MmrDelta, MmrPeaks, MmrProof, SmtLeaf,
160                SmtProof,
161            },
162            rand::{FeltRng, RpoRandomCoin},
163        },
164    };
165}
166
167pub use errors::{AuthenticationError, ClientError, IdPrefixFetchError};
168pub use miden_objects::{Felt, ONE, StarkField, Word, ZERO};
169pub use miden_proving_service_client::proving_service::tx_prover::RemoteTransactionProver;
170
171/// Provides various utilities that are commonly used throughout the Miden
172/// client library.
173pub mod utils {
174    pub use miden_tx::utils::{
175        ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
176        bytes_to_hex_string,
177        sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard},
178    };
179}
180
181/// Provides test utilities for working with accounts and account IDs
182/// within the Miden network. This module is only available when the `testing` feature is
183/// enabled.
184#[cfg(feature = "testing")]
185pub mod testing {
186    pub use miden_objects::testing::*;
187}
188
189use alloc::sync::Arc;
190
191use miden_objects::crypto::rand::FeltRng;
192use miden_tx::{
193    DataStore, LocalTransactionProver, TransactionExecutor, auth::TransactionAuthenticator,
194};
195use rand::RngCore;
196use rpc::NodeRpcClient;
197use store::{Store, data_store::ClientDataStore};
198use tracing::info;
199
200// MIDEN CLIENT
201// ================================================================================================
202
203/// A light client for connecting to the Miden network.
204///
205/// Miden client is responsible for managing a set of accounts. Specifically, the client:
206/// - Keeps track of the current and historical states of a set of accounts and related objects such
207///   as notes and transactions.
208/// - Connects to a Miden node to periodically sync with the current state of the network.
209/// - Executes, proves, and submits transactions to the network as directed by the user.
210pub struct Client {
211    /// The client's store, which provides a way to write and read entities to provide persistence.
212    store: Arc<dyn Store>,
213    /// An instance of [`FeltRng`] which provides randomness tools for generating new keys,
214    /// serial numbers, etc.
215    rng: ClientRng,
216    /// An instance of [`NodeRpcClient`] which provides a way for the client to connect to the
217    /// Miden node.
218    rpc_api: Arc<dyn NodeRpcClient + Send>,
219    /// An instance of a [`LocalTransactionProver`] which will be the default prover for the
220    /// client.
221    tx_prover: Arc<LocalTransactionProver>,
222    /// An instance of a [`TransactionExecutor`] that will be used to execute transactions.
223    tx_executor: TransactionExecutor,
224    /// Flag to enable the debug mode for scripts compilation and execution.
225    in_debug_mode: bool,
226}
227
228/// Construction and access methods.
229impl Client {
230    // CONSTRUCTOR
231    // --------------------------------------------------------------------------------------------
232
233    /// Returns a new instance of [`Client`].
234    ///
235    /// ## Arguments
236    ///
237    /// - `api`: An instance of [`NodeRpcClient`] which provides a way for the client to connect to
238    ///   the Miden node.
239    /// - `store`: An instance of [`Store`], which provides a way to write and read entities to
240    ///   provide persistence.
241    /// - `executor_store`: An instance of [`Store`] that provides a way for [`TransactionExecutor`]
242    ///   to retrieve relevant inputs at the moment of transaction execution. It should be the same
243    ///   store as the one for `store`, but it doesn't have to be the **same instance**.
244    /// - `authenticator`: Defines the transaction authenticator that will be used by the
245    ///   transaction executor whenever a signature is requested from within the VM.
246    /// - `in_debug_mode`: Instantiates the transaction executor (and in turn, its compiler) in
247    ///   debug mode, which will enable debug logs for scripts compiled with this mode for easier
248    ///   MASM debugging.
249    ///
250    /// # Errors
251    ///
252    /// Returns an error if the client couldn't be instantiated.
253    pub fn new(
254        rpc_api: Arc<dyn NodeRpcClient + Send>,
255        rng: Box<dyn FeltRng>,
256        store: Arc<dyn Store>,
257        authenticator: Arc<dyn TransactionAuthenticator>,
258        in_debug_mode: bool,
259    ) -> Self {
260        let data_store = Arc::new(ClientDataStore::new(store.clone())) as Arc<dyn DataStore>;
261        let authenticator = Some(authenticator);
262        let mut tx_executor = TransactionExecutor::new(data_store, authenticator);
263        let tx_prover = Arc::new(LocalTransactionProver::default());
264
265        if in_debug_mode {
266            info!("Creating the Client in debug mode.");
267            tx_executor = tx_executor.with_debug_mode();
268        }
269
270        Self {
271            store,
272            rng: ClientRng::new(rng),
273            rpc_api,
274            tx_prover,
275            tx_executor,
276            in_debug_mode,
277        }
278    }
279
280    /// Returns true if the client is in debug mode.
281    pub fn is_in_debug_mode(&self) -> bool {
282        self.in_debug_mode
283    }
284
285    /// Returns a reference to the client's random number generator. This can be used to generate
286    /// randomness for various purposes such as serial numbers, keys, etc.
287    pub fn rng(&mut self) -> &mut ClientRng {
288        &mut self.rng
289    }
290
291    // TEST HELPERS
292    // --------------------------------------------------------------------------------------------
293
294    #[cfg(any(test, feature = "testing"))]
295    pub fn test_rpc_api(&mut self) -> &mut Arc<dyn NodeRpcClient + Send> {
296        &mut self.rpc_api
297    }
298
299    #[cfg(any(test, feature = "testing"))]
300    pub fn test_store(&mut self) -> &mut Arc<dyn Store> {
301        &mut self.store
302    }
303}
304
305// CLIENT RNG
306// ================================================================================================
307
308/// A wrapper around a [`FeltRng`] that implements the [`RngCore`] trait.
309/// This allows the user to pass their own generic RNG so that it's used by the client.
310pub struct ClientRng(Box<dyn FeltRng>);
311
312impl ClientRng {
313    pub fn new(rng: Box<dyn FeltRng>) -> Self {
314        Self(rng)
315    }
316
317    pub fn inner_mut(&mut self) -> &mut Box<dyn FeltRng> {
318        &mut self.0
319    }
320}
321
322impl RngCore for ClientRng {
323    fn next_u32(&mut self) -> u32 {
324        self.0.next_u32()
325    }
326
327    fn next_u64(&mut self) -> u64 {
328        self.0.next_u64()
329    }
330
331    fn fill_bytes(&mut self, dest: &mut [u8]) {
332        self.0.fill_bytes(dest);
333    }
334}
335
336impl FeltRng for ClientRng {
337    fn draw_element(&mut self) -> Felt {
338        self.0.draw_element()
339    }
340
341    fn draw_word(&mut self) -> Word {
342        self.0.draw_word()
343    }
344}