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 Aug 1, 2024
1 parent c224107 commit 10d71d2
Show file tree
Hide file tree
Showing 45 changed files with 3,419 additions and 178 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ jobs:
rustup +nightly component add clippy
- name: make - clippy
run: make clippy

clippy-wasm:
name: Clippy WASM
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main
- name: Install Rust with clippy
run: |
rustup update --no-self-update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup +nightly component add clippy
- name: make - clippy-wasm
run: make clippy-wasm

rustfmt:
name: rustfmt
Expand Down
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 = { default-features = false, git = "https://github.com/0xPolygonMiden/miden-base", branch = "next"}
miden-objects = { default-features = false, features = ["serde"], git = "https://github.com/0xPolygonMiden/miden-base", branch = "next" }
miden-tx = { default-features = false , git = "https://github.com/0xPolygonMiden/miden-base", branch = "next" }
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" }
45 changes: 27 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@ help: ## Show description of all commands

# --- Variables -----------------------------------------------------------------------------------

FEATURES_CLIENT="testing, concurrent"
FEATURES_CLI="testing, concurrent"
NODE_FEATURES_TESTING="testing"
FEATURES_WEB_CLIENT=--features "testing"
FEATURES_CLIENT=--features "testing, concurrent"
FEATURES_CLI=--features "testing, concurrent"
NODE_FEATURES_TESTING=--features "testing"
WARNINGS=RUSTDOCFLAGS="-D warnings"
NODE_BRANCH="next"

# --- Linting -------------------------------------------------------------------------------------

.PHONY: clippy
clippy: ## Runs Clippy with configs
cargo +nightly clippy --workspace --all-targets --features $(FEATURES_CLI) -- -D warnings
cargo +nightly clippy --workspace --exclude miden-client-web --all-targets $(FEATURES_CLI) -- -D warnings

.PHONY: clippy-wasm
clippy-wasm: ## Runs Clippy for the miden-client-web package
cargo +nightly clippy --package miden-client-web --target wasm32-unknown-unknown --all-targets $(FEATURES_WEB_CLIENT) -- -D warnings

.PHONY: fix
fix: ## Runs Fix with configs
cargo +nightly fix --allow-staged --allow-dirty --all-targets --features $(FEATURES_CLI)
cargo +nightly fix --workspace --exclude miden-client-web --allow-staged --allow-dirty --all-targets $(FEATURES_CLI)

.PHONY: fix-wasm
fix-wasm: ## Runs Fix for the miden-client-web package
cargo +nightly fix --package miden-client-web --target wasm32-unknown-unknown --allow-staged --allow-dirty --all-targets $(FEATURES_WEB_CLIENT)

.PHONY: format
format: ## Runs format using nightly toolchain
Expand All @@ -31,7 +40,7 @@ format-check: ## Runs format using nightly toolchain but only in check mode
cargo +nightly fmt --all --check

.PHONY: lint
lint: format fix clippy ## Runs all linting tasks at once (clippy, fixing, formatting)
lint: format fix clippy fix-wasm clippy-wasm ## Runs all linting tasks at once (clippy, fixing, formatting)

# --- Documentation site --------------------------------------------------------------------------

Expand All @@ -52,14 +61,14 @@ doc-serve: doc-deps ## Serve documentation site
.PHONY: doc
doc: ## Generates & checks rust documentation. You'll need `jq` in order for this to run.
@cd crates/rust-client && \
FEATURES=$$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "miden-client") | .features | keys[] | select(. != "web-tonic")' | tr '\n' ',') && \
FEATURES=$$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "miden-client") | .features | keys[] | select(. != "web-tonic" and . != "idxdb")' | tr '\n' ',') && \
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --features "$$FEATURES" --keep-going --release

# --- Testing -------------------------------------------------------------------------------------

.PHONY: test
test: ## Run tests
cargo nextest run --release --lib --features $(FEATURES_CLIENT)
cargo nextest run --workspace --exclude miden-client-web --release --lib $(FEATURES_CLIENT)

.PHONY: test-deps
test-deps: ## Install dependencies for tests
Expand All @@ -69,12 +78,12 @@ test-deps: ## Install dependencies for tests

.PHONY: integration-test
integration-test: ## Run integration tests
cargo nextest run --release --test=integration --features $(FEATURES_CLI) --no-default-features
cargo nextest run --workspace --exclude miden-client-web --release --test=integration $(FEATURES_CLI) --no-default-features

.PHONY: integration-test-full
integration-test-full: ## Run the integration test binary with ignored tests included
cargo nextest run --release --test=integration --features $(FEATURES_CLI)
cargo nextest run --release --test=integration --features $(FEATURES_CLI) --run-ignored ignored-only -- test_import_genesis_accounts_can_be_used_for_transactions
cargo nextest run --workspace --exclude miden-client-web --release --test=integration $(FEATURES_CLI)
cargo nextest run --workspace --exclude miden-client-web --release --test=integration $(FEATURES_CLI) --run-ignored ignored-only -- test_import_genesis_accounts_can_be_used_for_transactions

.PHONY: kill-node
kill-node: ## Kill node process
Expand All @@ -89,31 +98,31 @@ node: ## Setup node directory
if [ -d miden-node ]; then cd miden-node ; else git clone https://github.com/0xPolygonMiden/miden-node.git && cd miden-node; fi
cd miden-node && git checkout $(NODE_BRANCH) && git pull origin $(NODE_BRANCH) && cargo update
cd miden-node && rm -rf miden-store.sqlite3*
cd miden-node && cargo run --bin miden-node --features $(NODE_FEATURES_TESTING) -- make-genesis --inputs-path ../tests/config/genesis.toml --force
cd miden-node && cargo run --bin miden-node $(NODE_FEATURES_TESTING) -- make-genesis --inputs-path ../tests/config/genesis.toml --force

.PHONY: start-node
start-node: ## Run node. This requires the node repo to be present at `miden-node`
cd miden-node && cargo run --bin miden-node --features $(NODE_FEATURES_TESTING) -- start --config ../tests/config/miden-node.toml node
cd miden-node && cargo run --bin miden-node $(NODE_FEATURES_TESTING) -- start --config ../tests/config/miden-node.toml node

# --- Installing ----------------------------------------------------------------------------------

install: ## Installs the CLI binary
cargo install --features $(FEATURES_CLI) --path bin/miden-cli
cargo install $(FEATURES_CLI) --path bin/miden-cli

# --- Building ------------------------------------------------------------------------------------

build: ## Builds the CLI binary and client library in release mode
cargo build --release --features $(FEATURES_CLI)
cargo build --workspace --exclude miden-client-web --release $(FEATURES_CLI)

build-wasm: ## Builds the client library for wasm32
cargo build --target wasm32-unknown-unknown --features idxdb,web-tonic --no-default-features --package miden-client
cargo build --package miden-client-web --target wasm32-unknown-unknown $(FEATURES_WEB_CLIENT)

# --- Check ---------------------------------------------------------------------------------------

.PHONY: check
check: ## Builds the CLI binary and client library in release mode
cargo check --release --features $(FEATURES_CLI)
cargo check --workspace --exclude miden-client-web --release $(FEATURES_CLI)

.PHONY: check-wasm
check-wasm: ## Builds the client library for wasm32
cargo check --target wasm32-unknown-unknown --features idxdb,web-tonic --no-default-features --package miden-client
cargo check --package miden-client-web --target wasm32-unknown-unknown $(FEATURES_WEB_CLIENT)
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,
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
57 changes: 1 addition & 56 deletions bin/miden-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ use miden_client::{
auth::{StoreAuthenticator, TransactionAuthenticator},
crypto::{FeltRng, RpoRandomCoin},
rpc::{NodeRpcClient, TonicRpcClient},
store::{
sqlite_store::SqliteStore, InputNoteRecord, NoteFilter as ClientNoteFilter,
OutputNoteRecord, Store,
},
store::{sqlite_store::SqliteStore, NoteFilter as ClientNoteFilter, OutputNoteRecord, Store},
Client, ClientError, Felt, IdPrefixFetchError,
};
use rand::Rng;
Expand Down Expand Up @@ -155,58 +152,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
48 changes: 2 additions & 46 deletions bin/miden-cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ use figment::{
Figment,
};
use miden_client::{
accounts::AccountId,
auth::TransactionAuthenticator,
crypto::FeltRng,
notes::{NoteError, NoteExecutionHint, NoteTag, NoteType},
rpc::NodeRpcClient,
store::Store,
Client,
accounts::AccountId, auth::TransactionAuthenticator, crypto::FeltRng, rpc::NodeRpcClient,
store::Store, Client,
};
use tracing::info;

Expand Down Expand Up @@ -139,45 +134,6 @@ 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),
}
}

/// Returns the token symbol map from the config file.
pub fn load_token_map() -> Result<TokenSymbolMap, String> {
let (config, _) = load_config_file()?;
Expand Down
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 = { default-features = false, git = "https://github.com/0xPolygonMiden/miden-base", branch = "next"}
miden-objects = { default-features = false, features = ["serde"], git = "https://github.com/0xPolygonMiden/miden-base", branch = "next" }
miden-tx = { default-features = false , git = "https://github.com/0xPolygonMiden/miden-base", branch = "next" }
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
9 changes: 5 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,10 @@ impl<N: NodeRpcClient, R: FeltRng, S: Store, A: TransactionAuthenticator> Client
&mut self.rpc_api
}

#[cfg(any(test, feature = "testing"))]
// TODO: the idxdb feature access here is temporary and should be removed in the future once
// a good solution to the syncrhonous store access in the store authenticator is found.
// https://github.com/0xPolygonMiden/miden-base/issues/705
#[cfg(any(test, feature = "testing", feature = "idxdb"))]
pub fn store(&mut self) -> &S {
&self.store
}
Expand Down
Loading

0 comments on commit 10d71d2

Please sign in to comment.