Skip to content

Commit ed3d005

Browse files
authored
feat: Note Transport integration (0xMiden#1296)
* Transport init * Rebase fix * Transport layer CLI * Move fetch-notes to update-based approach * Transport layer proto crate with no-default-features * Move to batched-tag note-fetching * IndexedDB transport methods * Relax CI * `TransportError` renamed to `NoteTransportError` * Do not use `Endpoint` with Transport layer * Fallible Transport methods if client not configured * Replace transport error `Other` variant for `TransportLayer` (server side error) * Transport layer update return input note * Change tower timeout for tonic's * Relax CI * Drop "Canonical", "Layer", from note transport naming * Use `Store` settings to store note transport cursor * `transport` files, mods renaming, remove schema cursor setting * Bump web-client SDK version * Remove unused `fetch_private_notes` fn generic `<I>` * Expand comments, cursor type * Move CLI note-transport commands to notes, flatten related commands * Add CLI `init` cmd note-transport-endpoint config option * Do transport protobuf codegen locally * Remove `tsconfig.tsbuildinfo` file * Fix proto WASM compilation, continue renaming private to note
1 parent 5c8ae59 commit ed3d005

File tree

31 files changed

+1897
-28
lines changed

31 files changed

+1897
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* [BREAKING] Added `block_to` parameter to `NodeRpcClient::sync_nullifiers` for better pagination control ([#1309](https://github.com/0xMiden/miden-client/pull/1309)).
2424
* [BREAKING] Removed `web-tonic` feature ([#1268](https://github.com/0xMiden/miden-client/pull/1268)).
2525
* [BREAKING] Updated Web Client account store functions from insert to upsert ([#1274](https://github.com/0xMiden/miden-client/pull/1274)).
26+
* [BREAKING] Added connectivity to the Transport Layer, adding a new `Client` field and `Store` methods ([#1296](https://github.com/0xMiden/miden-client/pull/1296)).
2627
* Removed `miden-lib` and `miden-objects` dependencies from web client & cli ([#1333](https://github.com/0xMiden/miden-client/pull/1333)).
2728
* Add more context to errors when deserializing objects ([#1336](https://github.com/0xMiden/miden-client/pull/1336))
2829
* [BREAKING] Renamed `TonicRpcClient` to `GrpcClient` and `tonic_rpc_client()` method to `grpc_client()` ([#1360](https://github.com/0xMiden/miden-client/pull/1360)).

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ miden-node-proto-build = { branch = "next", default-features = false, git = "htt
3737
miden-node-rpc = { branch = "next", git = "https://github.com/0xMiden/miden-node" }
3838
miden-node-store = { branch = "next", git = "https://github.com/0xMiden/miden-node" }
3939
miden-node-utils = { branch = "next", git = "https://github.com/0xMiden/miden-node" }
40+
miden-note-transport-proto-build = { default-features = false, git = "https://github.com/0xMiden/miden-note-transport" }
4041
miden-remote-prover = { branch = "next", features = ["concurrent"], git = "https://github.com/0xMiden/miden-node" }
4142
miden-remote-prover-client = { branch = "next", default-features = false, features = [
4243
"tx-prover",

bin/miden-cli/src/commands/init.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use clap::Parser;
66
use tracing::info;
77

88
use crate::CLIENT_CONFIG_FILE_NAME;
9-
use crate::config::{CliConfig, CliEndpoint, Network};
9+
use crate::config::{CliConfig, CliEndpoint, Network, NoteTransportConfig};
1010
use crate::errors::CliError;
1111

1212
/// Contains the account component template file generated on build.rs, corresponding to the basic
@@ -50,6 +50,13 @@ pub struct InitCmd {
5050
#[arg(long)]
5151
remote_prover_endpoint: Option<String>,
5252

53+
/// RPC endpoint for the note transport node. Required to use the note transport network to
54+
/// exchange private notes.
55+
/// The endpoint must be in the form of "{protocol}://{hostname}:{port}", being the protocol
56+
/// and port optional.
57+
#[arg(long)]
58+
note_transport_endpoint: Option<String>,
59+
5360
/// Maximum number of blocks the client can be behind the network.
5461
#[clap(long)]
5562
block_delta: Option<u32>,
@@ -80,6 +87,12 @@ impl InitCmd {
8087
None => None,
8188
};
8289

90+
cli_config.note_transport =
91+
self.note_transport_endpoint.as_ref().map(|rpc| NoteTransportConfig {
92+
endpoint: rpc.to_string(),
93+
..Default::default()
94+
});
95+
8396
cli_config.max_block_number_delta = self.block_delta;
8497

8598
let config_as_toml_string = toml::to_string_pretty(&cli_config).map_err(|err| {

bin/miden-cli/src/commands/notes.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use clap::ValueEnum;
22
use comfy_table::{Attribute, Cell, ContentArrangement, Table, presets};
3+
use miden_client::address::Address;
34
use miden_client::asset::Asset;
45
use miden_client::auth::TransactionAuthenticator;
56
use miden_client::note::{
7+
Note,
68
NoteConsumability,
9+
NoteId,
710
NoteInputs,
811
NoteMetadata,
912
WellKnownNote,
@@ -57,12 +60,21 @@ pub struct NotesCmd {
5760
/// consumable by this account will be shown.
5861
#[arg(short, long, value_name = "account_id")]
5962
account_id: Option<String>,
63+
/// Send a stored private note through the note transport network.
64+
/// Define both the note ID (as hex string) and address (as Bech32 string) such as:
65+
/// `--send 0xc1234567 mm1qpkdyek2c0ywwvzupakc7zlzty8qn2qnfc`
66+
#[arg(long, group = "action", num_args = 2, value_names = ["note_id", "address"])]
67+
send: Option<Vec<String>>,
68+
/// Fetch notes from the note transport network.
69+
/// Fetched notes for tracked note tags will be added to the store.
70+
#[arg(long, group = "action")]
71+
fetch: bool,
6072
}
6173

6274
impl NotesCmd {
6375
pub async fn execute<AUTH: TransactionAuthenticator + Sync>(
6476
&self,
65-
client: Client<AUTH>,
77+
mut client: Client<AUTH>,
6678
) -> Result<(), CliError> {
6779
match self {
6880
NotesCmd { list: Some(NoteFilter::Consumable), .. } => {
@@ -78,6 +90,14 @@ impl NotesCmd {
7890
NotesCmd { show: Some(id), .. } => {
7991
show_note(client, id.to_owned(), self.with_code).await?;
8092
},
93+
NotesCmd { send: Some(args), .. } => {
94+
let note_id = &args[0];
95+
let address = &args[1];
96+
send(&mut client, note_id, address).await?;
97+
},
98+
NotesCmd { fetch: true, .. } => {
99+
fetch(&mut client).await?;
100+
},
81101
_ => {
82102
list_notes(client, ClientNoteFilter::All).await?;
83103
},
@@ -311,6 +331,43 @@ async fn list_consumable_notes<AUTH: TransactionAuthenticator + Sync>(
311331
Ok(())
312332
}
313333

334+
// SEND
335+
// ================================================================================================
336+
337+
/// Send a (stored) note
338+
async fn send<AUTH: TransactionAuthenticator + Sync>(
339+
client: &mut Client<AUTH>,
340+
note_id: &str,
341+
address: &str,
342+
) -> Result<(), CliError> {
343+
let id = NoteId::try_from_hex(note_id).map_err(|e| CliError::Input(e.to_string()))?;
344+
let note_record = client
345+
.get_input_note(id)
346+
.await?
347+
.ok_or_else(|| CliError::Input(format!("note {note_id} not found")))?;
348+
let note: Note = note_record
349+
.try_into()
350+
.map_err(|e| CliError::Client(ClientError::NoteRecordConversionError(e)))?;
351+
let (_netid, address) =
352+
Address::from_bech32(address).map_err(|e| CliError::Input(e.to_string()))?;
353+
354+
client.send_private_note(note, &address).await?;
355+
356+
Ok(())
357+
}
358+
359+
// FETCH
360+
// ================================================================================================
361+
362+
/// Retrieve notes for all tracked tags
363+
///
364+
/// Fetched notes are stored in the store.
365+
async fn fetch<AUTH>(client: &mut Client<AUTH>) -> Result<(), CliError> {
366+
client.fetch_private_notes().await?;
367+
368+
Ok(())
369+
}
370+
314371
// HELPERS
315372
// ================================================================================================
316373
fn print_notes_summary<I>(notes: I, header: &str)

bin/miden-cli/src/config.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::str::FromStr;
55

66
use figment::value::{Dict, Map};
77
use figment::{Metadata, Profile, Provider};
8+
use miden_client::note_transport::NOTE_TRANSPORT_DEFAULT_ENDPOINT;
89
use miden_client::rpc::Endpoint;
910
use serde::{Deserialize, Serialize};
1011

@@ -33,6 +34,8 @@ pub struct CliConfig {
3334
/// Maximum number of blocks the client can be behind the network for transactions and account
3435
/// proofs to be considered valid.
3536
pub max_block_number_delta: Option<u32>,
37+
/// Describes settings related to the note transport endpoint.
38+
pub note_transport: Option<NoteTransportConfig>,
3639
}
3740

3841
// Make `ClientConfig` a provider itself for composability.
@@ -67,6 +70,7 @@ impl Default for CliConfig {
6770
remote_prover_endpoint: None,
6871
component_template_directory: Path::new(DEFAULT_COMPONENT_TEMPLATE_DIR).to_path_buf(),
6972
max_block_number_delta: None,
73+
note_transport: None,
7074
}
7175
}
7276
}
@@ -92,6 +96,27 @@ impl Default for RpcConfig {
9296
}
9397
}
9498

99+
// NOTE TRANSPORT CONFIG
100+
// ================================================================================================
101+
102+
/// Settings for the note transport client.
103+
#[derive(Debug, Deserialize, Serialize)]
104+
pub struct NoteTransportConfig {
105+
/// Address of the Miden Note Transport node to connect to.
106+
pub endpoint: String,
107+
/// Timeout for the Note Transport RPC api requests, in milliseconds.
108+
pub timeout_ms: u64,
109+
}
110+
111+
impl Default for NoteTransportConfig {
112+
fn default() -> Self {
113+
Self {
114+
endpoint: NOTE_TRANSPORT_DEFAULT_ENDPOINT.to_string(),
115+
timeout_ms: 10000,
116+
}
117+
}
118+
}
119+
95120
// CLI ENDPOINT
96121
// ================================================================================================
97122

bin/miden-cli/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use miden_client::account::AccountHeader;
99
use miden_client::auth::TransactionAuthenticator;
1010
use miden_client::builder::ClientBuilder;
1111
use miden_client::keystore::FilesystemKeyStore;
12+
use miden_client::note_transport::grpc::GrpcNoteTransportClient;
1213
use miden_client::store::{NoteFilter as ClientNoteFilter, OutputNoteRecord};
1314
use miden_client::{Client, DebugMode, IdPrefixFetchError};
1415
use miden_client_sqlite_store::SqliteStore;
@@ -187,6 +188,16 @@ impl Cli {
187188
builder = builder.max_block_number_delta(delta);
188189
}
189190

191+
if let Some(tl_config) = cli_config.note_transport {
192+
let client = GrpcNoteTransportClient::connect(
193+
tl_config.endpoint.to_string(),
194+
tl_config.timeout_ms,
195+
)
196+
.await
197+
.map_err(|e| CliError::Client(e.into()))?;
198+
builder = builder.note_transport(Arc::new(client));
199+
}
200+
190201
let client = builder.build().await?;
191202

192203
// Execute CLI command

bin/miden-cli/tests/cli.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ async fn create_rust_client_with_store_path(
764764
)?,
765765
None,
766766
None,
767+
None,
767768
)
768769
.await?,
769770
keystore,

crates/rust-client/Cargo.toml

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,35 @@ miden-testing = { optional = true, workspace = true }
4040
miden-tx = { workspace = true }
4141

4242
# External dependencies
43-
anyhow = { workspace = true }
44-
async-trait = { workspace = true }
45-
chrono = { optional = false, version = "0.4" }
46-
hex = { workspace = true }
47-
prost = { default-features = false, features = ["derive"], version = "0.13" }
48-
rand = { workspace = true }
49-
thiserror = { workspace = true }
50-
tonic = { default-features = false, features = ["codegen", "prost"], version = "0.13" }
51-
tracing = { workspace = true }
52-
uuid = { features = ["js", "serde", "v4"], optional = true, version = "1.10" }
43+
anyhow = { workspace = true }
44+
async-trait = { workspace = true }
45+
chrono = { optional = false, version = "0.4" }
46+
futures = { version = "0.3" }
47+
hex = { workspace = true }
48+
prost = { default-features = false, features = ["derive"], version = "0.13" }
49+
prost-types = { version = "0.13" }
50+
rand = { workspace = true }
51+
thiserror = { workspace = true }
52+
tonic = { default-features = false, features = ["codegen", "prost"], version = "0.13" }
53+
tonic-health = { version = "0.13" }
54+
tracing = { workspace = true }
55+
uuid = { features = ["js", "serde", "v4"], optional = true, version = "1.10" }
5356

5457
[target.'cfg(target_arch = "wasm32")'.dependencies]
5558
getrandom = { features = ["wasm_js"], version = "0.3" }
5659
tonic-web-wasm-client = { default-features = false, version = "0.7" }
5760
web-sys = { features = ["Storage", "Window", "console"], version = "0.3" }
5861

5962
[build-dependencies]
60-
miden-lib = { workspace = true }
61-
miden-node-proto-build = { workspace = true }
62-
miden-objects = { workspace = true }
63-
miette = { workspace = true }
64-
prost = { default-features = false, features = ["derive"], version = "0.13" }
65-
prost-build = { default-features = false, version = "0.13" }
66-
protox = { version = "0.7" }
67-
tonic-build = { version = "0.13" }
63+
miden-lib = { workspace = true }
64+
miden-node-proto-build = { workspace = true }
65+
miden-note-transport-proto-build = { workspace = true }
66+
miden-objects = { workspace = true }
67+
miette = { workspace = true }
68+
prost = { default-features = false, features = ["derive"], version = "0.13" }
69+
prost-build = { default-features = false, version = "0.13" }
70+
protox = { version = "0.7" }
71+
tonic-build = { version = "0.13" }
6872

6973
[dev-dependencies]
7074
miden-client = { features = ["std", "testing"], path = "." }

crates/rust-client/build.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ use std::fs;
22
use std::io::Write;
33

44
use miden_node_proto_build::rpc_api_descriptor;
5+
use miden_note_transport_proto_build::mnt_api_descriptor;
56
use miette::IntoDiagnostic;
67

78
const STD_PROTO_OUT_DIR: &str = "src/rpc/generated/std";
89
const NO_STD_PROTO_OUT_DIR: &str = "src/rpc/generated/nostd";
10+
const NOTE_TRANSPORT_STD_PROTO_OUT_DIR: &str = "src/note_transport/generated/std";
11+
const NOTE_TRANSPORT_NO_STD_PROTO_OUT_DIR: &str = "src/note_transport/generated/nostd";
912

1013
/// Defines whether the build script should generate files in `/src`.
1114
/// The docs.rs build pipeline has a read-only filesystem, so we have to avoid writing to `src`,
@@ -19,12 +22,47 @@ fn main() -> miette::Result<()> {
1922
}
2023

2124
compile_tonic_client_proto()?;
25+
compile_tonic_note_transport_proto()?;
2226
replace_no_std_types(NO_STD_PROTO_OUT_DIR.to_string() + "/rpc.rs");
2327
replace_no_std_types(NO_STD_PROTO_OUT_DIR.to_string() + "/rpc_store.rs");
2428
replace_no_std_types(NO_STD_PROTO_OUT_DIR.to_string() + "/block_producer.rs");
29+
replace_no_std_types(
30+
NOTE_TRANSPORT_NO_STD_PROTO_OUT_DIR.to_string() + "/miden_note_transport.rs",
31+
);
2532

2633
Ok(())
2734
}
35+
36+
// NOTE TRANSPORT CLIENT PROTO CODEGEN
37+
// ===============================================================================================
38+
39+
/// Generates the Rust protobuf bindings for the Note Transport client.
40+
fn compile_tonic_note_transport_proto() -> miette::Result<()> {
41+
let file_descriptors = mnt_api_descriptor();
42+
43+
let mut prost_config = prost_build::Config::new();
44+
prost_config.skip_debug(["AccountId", "Digest"]);
45+
46+
let mut web_tonic_prost_config = prost_build::Config::new();
47+
web_tonic_prost_config.skip_debug(["AccountId", "Digest"]);
48+
49+
// Generate the header of the user facing server from its proto file
50+
tonic_build::configure()
51+
.build_transport(false)
52+
.build_server(false)
53+
.out_dir(NOTE_TRANSPORT_NO_STD_PROTO_OUT_DIR)
54+
.compile_fds_with_config(web_tonic_prost_config, file_descriptors.clone())
55+
.into_diagnostic()?;
56+
57+
tonic_build::configure()
58+
.build_server(false)
59+
.out_dir(NOTE_TRANSPORT_STD_PROTO_OUT_DIR)
60+
.compile_fds_with_config(prost_config, file_descriptors)
61+
.into_diagnostic()?;
62+
63+
Ok(())
64+
}
65+
2866
// NODE RPC CLIENT PROTO CODEGEN
2967
// ===============================================================================================
3068

0 commit comments

Comments
 (0)