Skip to content

Commit

Permalink
Add Web Client
Browse files Browse the repository at this point in the history
  • Loading branch information
Dennis Garcia committed Jul 19, 2024
1 parent 96bef39 commit fab4ec7
Show file tree
Hide file tree
Showing 60 changed files with 3,803 additions and 187 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ debug/
target/

miden-node/
dist/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ resolver = "2"
members = [
"bin/miden-cli",
"crates/rust-client",
"crates/web-client",
"tests"
]

Expand All @@ -15,6 +16,11 @@ authors = ["miden contributors"]
repository = "https://github.com/0xPolygonMiden/miden-client"

[workspace.dependencies]
miden-lib = { version = "0.4", default-features = false }
miden-objects = { version = "0.4", default-features = false, features = ["serde"] }
miden-tx = { version = "0.4", default-features = false }
rand = { version = "0.8" }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["raw_value"] }
tokio = { version = "1.38", features = ["rt-multi-thread", "net", "macros"] }
tracing = { version = "0.1" }
10 changes: 4 additions & 6 deletions bin/miden-cli/src/commands/new_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use miden_client::{
assets::{Asset, FungibleAsset},
auth::TransactionAuthenticator,
crypto::{Digest, FeltRng},
notes::{NoteId, NoteType as MidenNoteType},
notes::{get_input_note_with_id_prefix, NoteId, NoteType as MidenNoteType},
rpc::NodeRpcClient,
store::Store,
transactions::{
build_swap_tag,
transaction_request::{PaymentTransactionData, SwapTransactionData, TransactionTemplate},
TransactionResult,
},
Expand All @@ -18,11 +19,8 @@ use miden_client::{
use tracing::info;

use crate::{
create_dynamic_table, get_input_note_with_id_prefix,
utils::{
build_swap_tag, get_input_acc_id_by_prefix_or_default, parse_account_id,
parse_fungible_asset,
},
create_dynamic_table,
utils::{get_input_acc_id_by_prefix_or_default, parse_account_id, parse_fungible_asset},
};

#[derive(Debug, Clone, Copy, ValueEnum)]
Expand Down
6 changes: 2 additions & 4 deletions bin/miden-cli/src/commands/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ use miden_client::{
assets::Asset,
auth::TransactionAuthenticator,
crypto::{Digest, FeltRng},
notes::{NoteConsumability, NoteInputs, NoteMetadata},
notes::{get_input_note_with_id_prefix, NoteConsumability, NoteInputs, NoteMetadata},
rpc::NodeRpcClient,
store::{InputNoteRecord, NoteFilter as ClientNoteFilter, OutputNoteRecord, Store},
transactions::known_script_roots::{P2ID, P2IDR, SWAP},
Client, ClientError, IdPrefixFetchError,
};

use crate::{
create_dynamic_table, get_input_note_with_id_prefix, get_output_note_with_id_prefix, Parser,
};
use crate::{create_dynamic_table, get_output_note_with_id_prefix, Parser};

#[derive(Clone, Debug, ValueEnum)]
pub enum NoteFilter {
Expand Down
52 changes: 0 additions & 52 deletions bin/miden-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,58 +154,6 @@ pub fn create_dynamic_table(headers: &[&str]) -> Table {
table
}

/// Returns the client input note whose ID starts with `note_id_prefix`
///
/// # Errors
///
/// - Returns [IdPrefixFetchError::NoMatch] if we were unable to find any note where
/// `note_id_prefix` is a prefix of its id.
/// - Returns [IdPrefixFetchError::MultipleMatches] if there were more than one note found
/// where `note_id_prefix` is a prefix of its id.
pub(crate) fn get_input_note_with_id_prefix<
N: NodeRpcClient,
R: FeltRng,
S: Store,
A: TransactionAuthenticator,
>(
client: &Client<N, R, S, A>,
note_id_prefix: &str,
) -> Result<InputNoteRecord, IdPrefixFetchError> {
let mut input_note_records = client
.get_input_notes(ClientNoteFilter::All)
.map_err(|err| {
tracing::error!("Error when fetching all notes from the store: {err}");
IdPrefixFetchError::NoMatch(format!("note ID prefix {note_id_prefix}").to_string())
})?
.into_iter()
.filter(|note_record| note_record.id().to_hex().starts_with(note_id_prefix))
.collect::<Vec<_>>();

if input_note_records.is_empty() {
return Err(IdPrefixFetchError::NoMatch(
format!("note ID prefix {note_id_prefix}").to_string(),
));
}
if input_note_records.len() > 1 {
let input_note_record_ids = input_note_records
.iter()
.map(|input_note_record| input_note_record.id())
.collect::<Vec<_>>();
tracing::error!(
"Multiple notes found for the prefix {}: {:?}",
note_id_prefix,
input_note_record_ids
);
return Err(IdPrefixFetchError::MultipleMatches(
format!("note ID prefix {note_id_prefix}").to_string(),
));
}

Ok(input_note_records
.pop()
.expect("input_note_records should always have one element"))
}

/// Returns the client output note whose ID starts with `note_id_prefix`
///
/// # Errors
Expand Down
39 changes: 0 additions & 39 deletions bin/miden-cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,42 +132,3 @@ pub fn parse_fungible_asset(arg: &str) -> Result<(u64, AccountId), String> {

Ok((amount, faucet_id))
}

/// Returns a note tag for a swap note with the specified parameters.
///
/// Use case ID for the returned tag is set to 0.
///
/// Tag payload is constructed by taking asset tags (8 bits of faucet ID) and concatenating them
/// together as offered_asset_tag + requested_asset tag.
///
/// Network execution hint for the returned tag is set to `Local`.
///
/// Based on miden-base's implementation (<https://github.com/0xPolygonMiden/miden-base/blob/9e4de88031b55bcc3524cb0ccfb269821d97fb29/miden-lib/src/notes/mod.rs#L153>)
///
/// TODO: we should make the function in base public and once that gets released use that one and
/// delete this implementation.
pub fn build_swap_tag(
note_type: NoteType,
offered_asset_faucet_id: AccountId,
requested_asset_faucet_id: AccountId,
) -> Result<NoteTag, NoteError> {
const SWAP_USE_CASE_ID: u16 = 0;

// get bits 4..12 from faucet IDs of both assets, these bits will form the tag payload; the
// reason we skip the 4 most significant bits is that these encode metadata of underlying
// faucets and are likely to be the same for many different faucets.

let offered_asset_id: u64 = offered_asset_faucet_id.into();
let offered_asset_tag = (offered_asset_id >> 52) as u8;

let requested_asset_id: u64 = requested_asset_faucet_id.into();
let requested_asset_tag = (requested_asset_id >> 52) as u8;

let payload = ((offered_asset_tag as u16) << 8) | (requested_asset_tag as u16);

let execution = NoteExecutionHint::Local;
match note_type {
NoteType::Public => NoteTag::for_public_use_case(SWAP_USE_CASE_ID, payload, execution),
_ => NoteTag::for_local_use_case(SWAP_USE_CASE_ID, payload),
}
}
10 changes: 5 additions & 5 deletions crates/rust-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ chrono = { version = "0.4", optional = false }
getrandom = { version = "0.2", features = ["js"], optional = true }
hex = { version = "0.4" , optional = true}
lazy_static = { version = "1.5", optional = true }
miden-lib = { version = "0.4", default-features = false }
miden-objects = { version = "0.4", default-features = false, features = ["serde"] }
miden-tx = { version = "0.4", default-features = false }
miden-lib = { workspace = true }
miden-objects = { workspace = true }
miden-tx = { workspace = true }
prost = { version = "0.12", optional = true, default-features = false, features = ["derive"] }
rand = { workspace = true }
rusqlite = { version = "0.31", features = ["vtab", "array", "bundled"], optional = true }
rusqlite_migration = { version = "1.0", optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["raw_value"] }
serde = { workspace = true }
serde_json = { workspace = true }
serde-wasm-bindgen = { version = "0.6", optional = true }
thiserror = { version = "1.0", optional = true }
tokio = { workspace = true , optional = true }
Expand Down
6 changes: 2 additions & 4 deletions crates/rust-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ pub mod testing {
pub use miden_objects::accounts::account_id::testing::*;
}

use alloc::rc::Rc;
#[cfg(feature = "testing")]
use alloc::vec::Vec;
use alloc::{rc::Rc, vec::Vec};

use miden_objects::crypto::rand::FeltRng;
use miden_tx::{auth::TransactionAuthenticator, TransactionExecutor};
Expand Down Expand Up @@ -150,7 +148,7 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store, A: TransactionAuthenticator> Client
&mut self.rpc_api
}

#[cfg(any(test, feature = "testing"))]
#[cfg(any(test, feature = "testing", feature = "idxdb"))]
pub fn store(&mut self) -> &S {
&self.store
}
Expand Down
54 changes: 53 additions & 1 deletion crates/rust-client/src/notes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
InputNoteRecord, NoteFilter, NoteRecordDetails, NoteStatus, OutputNoteRecord, Store,
StoreError,
},
Client, ClientError,
Client, ClientError, IdPrefixFetchError,
};

mod note_screener;
Expand Down Expand Up @@ -308,3 +308,55 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store, A: TransactionAuthenticator> Client
.map_err(ClientError::TransactionExecutorError)
}
}

/// Returns the client input note whose ID starts with `note_id_prefix`
///
/// # Errors
///
/// - Returns [IdPrefixFetchError::NoMatch] if we were unable to find any note where
/// `note_id_prefix` is a prefix of its id.
/// - Returns [IdPrefixFetchError::MultipleMatches] if there were more than one note found
/// where `note_id_prefix` is a prefix of its id.
#[maybe_async]
pub fn get_input_note_with_id_prefix<
N: NodeRpcClient,
R: FeltRng,
S: Store,
A: TransactionAuthenticator,
>(
client: &Client<N, R, S, A>,
note_id_prefix: &str,
) -> Result<InputNoteRecord, IdPrefixFetchError> {
let mut input_note_records = maybe_await!(client.get_input_notes(NoteFilter::All))
.map_err(|err| {
tracing::error!("Error when fetching all notes from the store: {err}");
IdPrefixFetchError::NoMatch(format!("note ID prefix {note_id_prefix}").to_string())
})?
.into_iter()
.filter(|note_record| note_record.id().to_hex().starts_with(note_id_prefix))
.collect::<Vec<_>>();

if input_note_records.is_empty() {
return Err(IdPrefixFetchError::NoMatch(
format!("note ID prefix {note_id_prefix}").to_string(),
));
}
if input_note_records.len() > 1 {
let input_note_record_ids = input_note_records
.iter()
.map(|input_note_record| input_note_record.id())
.collect::<Vec<_>>();
tracing::error!(
"Multiple notes found for the prefix {}: {:?}",
note_id_prefix,
input_note_record_ids
);
return Err(IdPrefixFetchError::MultipleMatches(
format!("note ID prefix {note_id_prefix}").to_string(),
));
}

Ok(input_note_records
.pop()
.expect("input_note_records should always have one element"))
}
3 changes: 0 additions & 3 deletions crates/rust-client/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ use miden_objects::{
BlockHeader, Digest,
};

#[cfg(any(feature = "tonic", feature = "web-tonic"))]
mod domain;

#[cfg(feature = "tonic")]
mod tonic_client;
#[cfg(test)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ use core::fmt::{self, Debug, Display, Formatter};

use miden_objects::accounts::AccountId;

#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::account::AccountId as ProtoAccountId;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::account::AccountId as ProtoAccountId;
use crate::rpc::RpcConversionError;
use crate::rpc::{
tonic_client::generated::account::AccountId as ProtoAccountId, RpcConversionError,
};

// ACCOUNT ID
// ================================================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use miden_objects::BlockHeader;

use super::MissingFieldHelper;
#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::block_header;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::block_header;
use crate::rpc::RpcConversionError;
use crate::rpc::{tonic_client::generated::block_header, RpcConversionError};

// BLOCK HEADER
// ================================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ use core::fmt::{self, Debug, Display, Formatter};
use hex::ToHex;
use miden_objects::{notes::NoteId, Digest, Felt, StarkField};

#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::digest;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::digest;
use crate::rpc::RpcConversionError;
use crate::rpc::{tonic_client::generated::digest, RpcConversionError};

// CONSTANTS
// ================================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ use miden_objects::{
Digest,
};

#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated;
use crate::rpc::RpcConversionError;
use crate::rpc::{tonic_client::generated, RpcConversionError};

// MERKLE PATH
// ================================================================================================
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ use miden_objects::{
};

use super::MissingFieldHelper;
#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::note::NoteMetadata as ProtoNoteMetadata;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::note::NoteMetadata as ProtoNoteMetadata;
use crate::rpc::RpcConversionError;
use crate::rpc::{
tonic_client::generated::note::NoteMetadata as ProtoNoteMetadata, RpcConversionError,
};

impl TryFrom<ProtoNoteMetadata> for NoteMetadata {
type Error = RpcConversionError;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use miden_objects::{crypto::hash::rpo::RpoDigest, notes::Nullifier};

#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::digest::Digest;
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::digest::Digest;
use crate::rpc::RpcConversionError;
use crate::rpc::{tonic_client::generated::digest::Digest, RpcConversionError};

// INTO NULLIFIER
// ================================================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
use miden_objects::{crypto::hash::rpo::RpoDigest, transaction::TransactionId};

#[cfg(feature = "tonic")]
use crate::rpc::tonic_client::generated::{
digest::Digest, transaction::TransactionId as ProtoTransactionId,
use crate::rpc::{
tonic_client::generated::{digest::Digest, transaction::TransactionId as ProtoTransactionId},
RpcConversionError,
};
#[cfg(feature = "web-tonic")]
use crate::rpc::web_tonic_client::generated::{
digest::Digest, transaction::TransactionId as ProtoTransactionId,
};
use crate::rpc::RpcConversionError;

// INTO TRANSACTION ID
// ================================================================================================
Expand Down
2 changes: 2 additions & 0 deletions crates/rust-client/src/rpc/tonic_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use super::{
};
use crate::{config::RpcConfig, rpc::RpcError};

mod domain;

#[rustfmt::skip]
pub mod generated;

Expand Down
Loading

0 comments on commit fab4ec7

Please sign in to comment.