Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Web Client #437

Merged
merged 2 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v0.5.0 (TBD)

* Added the Web Client Crate
* [BREAKING] Refactored `Client` to merge submit_transaction and prove_transaction (#445)
* Tracked token symbols with config file (#441).
* [BREAKING] Refactored `TransactionRequest` to represent a generalized transaction (#438).
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" }
59 changes: 34 additions & 25 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: ## Run Clippy with configs
cargo +nightly clippy --workspace --all-targets --features $(FEATURES_CLI) -- -D warnings
clippy: ## Runs Clippy with configs
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: ## Run Fix with configs
cargo +nightly fix --allow-staged --allow-dirty --all-targets --features $(FEATURES_CLI)
fix: ## Runs Fix with configs
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: ## Run format using nightly toolchain
Expand All @@ -31,7 +40,7 @@ format-check: ## Run format using nightly toolchain but only in check mode
cargo +nightly fmt --all --check

.PHONY: lint
lint: format fix clippy ## Run 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: ## Generate & check 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: ## Install the CLI binary
cargo install --features $(FEATURES_CLI) --path bin/miden-cli
install: ## Installs the CLI binary
cargo install $(FEATURES_CLI) --path bin/miden-cli

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

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

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

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

.PHONY: check
check: ## Check CLI and std client for errors without code generation
cargo check --release --features $(FEATURES_CLI)
check: ## Builds the CLI binary and client library in release mode
cargo check --workspace --exclude miden-client-web --release $(FEATURES_CLI)

.PHONY: check-wasm
check-wasm: ## Check WASM client for errors without code generation
cargo check --target wasm32-unknown-unknown --features idxdb,web-tonic --no-default-features --package miden-client
check-wasm: ## Builds the client library for wasm32
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
Loading