From 659b4a0f103b951b774c7d0f1ef27dab99f4ce00 Mon Sep 17 00:00:00 2001 From: keinberger Date: Tue, 28 Apr 2026 12:36:27 +0300 Subject: [PATCH 1/5] fix: address rust tutorial issues from #191 --- .../rust-client/counter_contract_tutorial.md | 160 ++++++----------- .../creating_notes_in_masm_tutorial.md | 12 +- docs/src/rust-client/custom_note_how_to.md | 10 +- .../foreign_procedure_invocation_tutorial.md | 133 +++++--------- .../rust-client/mappings_in_masm_how_to.md | 76 +++----- .../network_transactions_tutorial.md | 170 ++++++------------ docs/src/rust-client/oracle_tutorial.md | 88 ++++----- .../public_account_interaction_tutorial.md | 112 +++--------- rust-client/Cargo.lock | 3 - rust-client/Cargo.toml | 3 - .../src/bin/counter_contract_deploy.rs | 62 +++---- rust-client/src/bin/counter_contract_fpi.rs | 63 +++---- .../src/bin/counter_contract_increment.rs | 49 ++--- .../src/bin/create_mint_consume_send.rs | 6 +- rust-client/src/bin/delegated_prover.rs | 6 +- rust-client/src/bin/hash_preimage_note.rs | 10 +- rust-client/src/bin/mapping_example.rs | 56 ++---- .../src/bin/network_notes_counter_contract.rs | 65 +++---- rust-client/src/bin/note_creation_in_masm.rs | 12 +- rust-client/src/bin/oracle_data_query.rs | 56 +++--- .../src/bin/unauthenticated_note_transfer.rs | 6 +- 21 files changed, 377 insertions(+), 781 deletions(-) diff --git a/docs/src/rust-client/counter_contract_tutorial.md b/docs/src/rust-client/counter_contract_tutorial.md index 866deb2b..deeb1765 100644 --- a/docs/src/rust-client/counter_contract_tutorial.md +++ b/docs/src/rust-client/counter_contract_tutorial.md @@ -54,50 +54,23 @@ In the previous section, we explained how to instantiate the Miden client. We ca Copy and paste the following code into your `src/main.rs` file: ```rust no_run -use miden_client::auth::NoAuth; -use miden_client::transaction::TransactionKernel; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ - address::NetworkId, - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, + account::{ + component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, + AccountType, StorageSlot, StorageSlotName, }, + address::NetworkId, + auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, transaction::TransactionRequestBuilder, - ClientError, + ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, - AccountType, StorageSlot, StorageSlotName, - }, - Word, -}; - -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} #[tokio::main] async fn main() -> Result<(), ClientError> { @@ -107,10 +80,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -256,15 +229,20 @@ To build the counter contract copy and paste the following code at the end of yo // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating counter contract."); -// Load the MASM file for the counter contract -let counter_path = Path::new("../masm/accounts/counter.masm"); -let counter_code = fs::read_to_string(counter_path).unwrap(); +// Load the MASM file for the counter contract. `include_str!` resolves at +// compile time relative to this source file, so the binary is independent of +// the working directory it is run from. +let counter_code = include_str!("../masm/accounts/counter.masm"); -// Compile the account code into `AccountComponent` with one storage slot +// Compile the account code into `AccountComponent` with one storage slot. +// Using `client.code_builder()` makes the assembler share the client's +// persisted source manager, which keeps debug spans coherent for any +// libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); -let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code) +let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code) .unwrap(); let counter_component = AccountComponent::new( component_code, @@ -324,21 +302,17 @@ Paste the following code at the end of your `src/main.rs` file: println!("\n[STEP 2] Call Counter Contract With Script"); // Load the MASM script referencing the increment procedure -let script_path = Path::new("../masm/scripts/counter_script.masm"); -let script_code = fs::read_to_string(script_path).unwrap(); - -// Create a library from the counter contract code -let account_component_lib = create_library( - "external_contract::counter_contract", - &counter_code, -) -.unwrap(); +let script_code = include_str!("../masm/scripts/counter_script.masm"); +// Compile the script with the counter contract code linked as a dynamic +// module on the same `CodeBuilder`. This shares the client's source +// manager between parsing and assembly, which is what miden-vm#2778 +// requires to avoid panics when debug spans are reported. let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script @@ -384,50 +358,23 @@ println!( The final `src/main.rs` file should look like this: ```rust no_run -use miden_client::auth::NoAuth; -use miden_client::transaction::TransactionKernel; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ - address::NetworkId, - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, + account::{ + component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, + AccountType, StorageSlot, StorageSlotName, }, + address::NetworkId, + auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, transaction::TransactionRequestBuilder, - ClientError, + ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, - AccountType, StorageSlot, StorageSlotName, - }, - Word, -}; - -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} #[tokio::main] async fn main() -> Result<(), ClientError> { @@ -437,10 +384,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -458,15 +405,20 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating counter contract."); - // Load the MASM file for the counter contract - let counter_path = Path::new("../masm/accounts/counter.masm"); - let counter_code = fs::read_to_string(counter_path).unwrap(); + // Load the MASM file for the counter contract. `include_str!` resolves at + // compile time relative to this source file, so the binary is independent + // of the working directory it is run from. + let counter_code = include_str!("../masm/accounts/counter.masm"); - // Compile the account code into `AccountComponent` with one storage slot + // Compile the account code into `AccountComponent` with one storage slot. + // Using `client.code_builder()` makes the assembler share the client's + // persisted source manager, which keeps debug spans coherent for any + // libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); - let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code) + let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code) .unwrap(); let counter_component = AccountComponent::new( component_code, @@ -503,21 +455,17 @@ async fn main() -> Result<(), ClientError> { println!("\n[STEP 2] Call Counter Contract With Script"); // Load the MASM script referencing the increment procedure - let script_path = Path::new("../masm/scripts/counter_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - // Create a library from the counter contract code - let account_component_lib = create_library( - "external_contract::counter_contract", - &counter_code, - ) - .unwrap(); + let script_code = include_str!("../masm/scripts/counter_script.masm"); + // Compile the script with the counter contract code linked as a dynamic + // module on the same `CodeBuilder`. This shares the client's source + // manager between parsing and assembly, which is what miden-vm#2778 + // requires to avoid panics when debug spans are reported. let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/docs/src/rust-client/creating_notes_in_masm_tutorial.md b/docs/src/rust-client/creating_notes_in_masm_tutorial.md index 4f831b41..f513160e 100644 --- a/docs/src/rust-client/creating_notes_in_masm_tutorial.md +++ b/docs/src/rust-client/creating_notes_in_masm_tutorial.md @@ -204,7 +204,7 @@ Copy and paste the following code into your `src/main.rs` file. ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration}; use miden_client::{ @@ -312,10 +312,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -395,13 +395,15 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Create iterative output note"); - let code = fs::read_to_string(Path::new("../masm/notes/iterative_output_note.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let code = include_str!("../masm/notes/iterative_output_note.masm"); let serial_num = client.rng().draw_word(); // Create note metadata and tag let tag = NoteTag::new(0); let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public).with_tag(tag); - let note_script = client.code_builder().compile_note_script(&code).unwrap(); + let note_script = client.code_builder().compile_note_script(code).unwrap(); let note_storage = NoteStorage::new(vec![ alice_account.id().prefix().as_felt(), alice_account.id().suffix(), diff --git a/docs/src/rust-client/custom_note_how_to.md b/docs/src/rust-client/custom_note_how_to.md index 7fc58599..4525917e 100644 --- a/docs/src/rust-client/custom_note_how_to.md +++ b/docs/src/rust-client/custom_note_how_to.md @@ -124,7 +124,7 @@ The following Rust code demonstrates how to implement the steps outlined above u ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration}; use miden_client::{ @@ -243,10 +243,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -334,7 +334,9 @@ async fn main() -> Result<(), ClientError> { let digest = Hasher::hash_elements(&secret_vals); println!("digest: {:?}", digest); - let code = fs::read_to_string(Path::new("../masm/notes/hash_preimage_note.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let code = include_str!("../masm/notes/hash_preimage_note.masm"); let serial_num = client.rng().draw_word(); let note_script = client.code_builder().compile_note_script(code).unwrap(); diff --git a/docs/src/rust-client/foreign_procedure_invocation_tutorial.md b/docs/src/rust-client/foreign_procedure_invocation_tutorial.md index f7df1084..23871ffb 100644 --- a/docs/src/rust-client/foreign_procedure_invocation_tutorial.md +++ b/docs/src/rust-client/foreign_procedure_invocation_tutorial.md @@ -125,47 +125,23 @@ end ```rust no_run use rand::RngCore; -use std::{fs, path::Path, sync::Arc, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use tokio::time::sleep; use miden_client::{ account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, + component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, AccountStorageMode, AccountType, StorageSlot, StorageSlotName, }, - account::AccountId, - assembly::{ - CodeBuilder, - DefaultSourceManager, - Library, - Module, - ModuleKind, - Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{domain::account::AccountStorageRequirements, Endpoint, GrpcClient}, - transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder}, + transaction::{ForeignAccount, TransactionRequestBuilder}, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -173,10 +149,10 @@ async fn main() -> Result<(), ClientError> { let timeout_ms = 10_000; let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -194,13 +170,15 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating count reader contract."); - let count_reader_path = Path::new("../masm/accounts/count_reader.masm"); - let count_reader_code = fs::read_to_string(count_reader_path).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let count_reader_code = include_str!("../masm/accounts/count_reader.masm"); let count_reader_slot_name = StorageSlotName::new("miden::tutorials::count_reader").expect("valid slot name"); - let count_reader_component_code = CodeBuilder::new() - .compile_component_code("external_contract::count_reader_contract", &count_reader_code) + let count_reader_component_code = client + .code_builder() + .compile_component_code("external_contract::count_reader_contract", count_reader_code) .unwrap(); let count_reader_component = AccountComponent::new( count_reader_component_code, @@ -297,13 +275,13 @@ Add this snippet to the end of your file in the `main()` function: println!("\n[STEP 3] Call counter contract with FPI from count reader contract"); // Derive the get_count procedure hash from the locally compiled counter library. -let counter_contract_path = Path::new("../masm/accounts/counter.masm"); -let counter_contract_code = fs::read_to_string(counter_contract_path).unwrap(); +let counter_contract_code = include_str!("../masm/accounts/counter.masm"); // Compile the counter as a component (same path as the deploy binary) to get // the correct procedure root that matches the on-chain MAST. -let counter_component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_contract_code) +let counter_component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_contract_code) .unwrap(); let counter_component = AccountComponent::new( counter_component_code, @@ -323,9 +301,7 @@ println!("get_count hash: {:?}", get_count_hash); println!("counter id prefix: {:?}", counter_contract_id.prefix()); println!("counter id suffix: {:?}", counter_contract_id.suffix()); -let script_path = Path::new("../masm/scripts/reader_script.masm"); -let script_code_original = fs::read_to_string(script_path).unwrap(); -let script_code = script_code_original +let script_code = include_str!("../masm/scripts/reader_script.masm") .replace("{get_count_proc_hash}", &get_count_hash) .replace( "{account_id_suffix}", @@ -336,17 +312,15 @@ let script_code = script_code_original &u64::from(counter_contract_id.prefix()).to_string(), ); -let account_component_lib = create_library( - "external_contract::count_reader_contract", - &count_reader_code, -) -.unwrap(); - +// Link the count reader contract code into the same `CodeBuilder` chain +// that compiles the script, so the assembler shares the client's +// persisted source manager (avoids the source-span mismatch from +// miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::count_reader_contract", count_reader_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code.as_str()) .unwrap(); let foreign_account = @@ -406,7 +380,7 @@ The final `src/main.rs` file should look like this: ```rust no_run use rand::RngCore; -use std::{fs, path::Path, sync::Arc, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use tokio::time::sleep; use miden_client::{ @@ -414,34 +388,15 @@ use miden_client::{ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, AccountStorageMode, AccountType, StorageSlot, StorageSlotName, }, - assembly::{ - CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind, - Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{domain::account::AccountStorageRequirements, Endpoint, GrpcClient}, - transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder}, + transaction::{ForeignAccount, TransactionRequestBuilder}, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -449,10 +404,10 @@ async fn main() -> Result<(), ClientError> { let timeout_ms = 10_000; let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -470,15 +425,17 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating count reader contract."); - let count_reader_path = Path::new("../masm/accounts/count_reader.masm"); - let count_reader_code = fs::read_to_string(count_reader_path).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let count_reader_code = include_str!("../masm/accounts/count_reader.masm"); let count_reader_slot_name = StorageSlotName::new("miden::tutorials::count_reader").expect("valid slot name"); - let count_reader_component_code = CodeBuilder::new() + let count_reader_component_code = client + .code_builder() .compile_component_code( "external_contract::count_reader_contract", - &count_reader_code, + count_reader_code, ) .unwrap(); let count_reader_component = AccountComponent::new( @@ -547,13 +504,13 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Call counter contract with FPI from count reader contract"); - let counter_contract_path = Path::new("../masm/accounts/counter.masm"); - let counter_contract_code = fs::read_to_string(counter_contract_path).unwrap(); + let counter_contract_code = include_str!("../masm/accounts/counter.masm"); // Compile the counter as a component (same path as the deploy binary) to get // the correct procedure root that matches the on-chain MAST. - let counter_component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_contract_code) + let counter_component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_contract_code) .unwrap(); let counter_component = AccountComponent::new( counter_component_code, @@ -573,9 +530,7 @@ async fn main() -> Result<(), ClientError> { println!("counter id prefix: {:?}", counter_contract_id.prefix()); println!("counter id suffix: {:?}", counter_contract_id.suffix()); - let script_path = Path::new("../masm/scripts/reader_script.masm"); - let script_code_original = fs::read_to_string(script_path).unwrap(); - let script_code = script_code_original + let script_code = include_str!("../masm/scripts/reader_script.masm") .replace("{get_count_proc_hash}", &get_count_hash) .replace( "{account_id_suffix}", @@ -586,17 +541,15 @@ async fn main() -> Result<(), ClientError> { &u64::from(counter_contract_id.prefix()).to_string(), ); - let account_component_lib = create_library( - "external_contract::count_reader_contract", - &count_reader_code, - ) - .unwrap(); - + // Link the count reader contract code into the same `CodeBuilder` chain + // that compiles the script, so the assembler shares the client's + // persisted source manager (avoids the source-span mismatch from + // miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::count_reader_contract", count_reader_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code.as_str()) .unwrap(); let foreign_account = diff --git a/docs/src/rust-client/mappings_in_masm_how_to.md b/docs/src/rust-client/mappings_in_masm_how_to.md index 46ab6e87..c9e37a21 100644 --- a/docs/src/rust-client/mappings_in_masm_how_to.md +++ b/docs/src/rust-client/mappings_in_masm_how_to.md @@ -145,49 +145,22 @@ The script calls the `write_to_map` procedure in the account which writes the ke Below is the Rust code that deploys the smart contract, creates the transaction script, and submits a transaction to update the mapping in the account: ```rust no_run -use miden_client::auth::NoAuth; -use miden_client::transaction::TransactionKernel; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, + account::{ + component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, + AccountType, StorageMap, StorageSlot, StorageSlotName, }, + auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, transaction::TransactionRequestBuilder, - ClientError, + ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, - AccountType, StorageMap, StorageSlot, StorageSlotName, - }, - Felt, Word, -}; - -fn create_library( - assembler: Assembler, - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} #[tokio::main] async fn main() -> Result<(), ClientError> { @@ -197,10 +170,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -218,12 +191,9 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Deploy a smart contract with a mapping"); - // Load the MASM file for the counter contract - let file_path = Path::new("../masm/accounts/mapping_example_contract.masm"); - let account_code = fs::read_to_string(file_path).unwrap(); - - // Prepare assembler (debug mode = true) - let assembler: Assembler = TransactionKernel::assembler(); + // Load the MASM file for the counter contract. `include_str!` resolves at + // compile time relative to this source file. + let account_code = include_str!("../masm/accounts/mapping_example_contract.masm"); // Using an empty storage value in slot 0 since this is usually reserved // for the account pub_key and metadata @@ -238,8 +208,9 @@ async fn main() -> Result<(), ClientError> { let storage_slot_map = StorageSlot::with_map(map_slot_name.clone(), storage_map.clone()); // Compile the account code into `AccountComponent` with one storage slot - let component_code = CodeBuilder::new() - .compile_component_code("miden_by_example::mapping_example_contract", &account_code) + let component_code = client + .code_builder() + .compile_component_code("miden_by_example::mapping_example_contract", account_code) .unwrap(); let mapping_contract_component = AccountComponent::new( component_code, @@ -271,23 +242,16 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Call Mapping Contract With Script"); - let script_code = - fs::read_to_string(Path::new("../masm/scripts/mapping_example_script.masm")).unwrap(); - - // Create the library from the account source code using the helper function. - let account_component_lib = create_library( - assembler.clone(), - "miden_by_example::mapping_example_contract", - &account_code, - ) - .unwrap(); + let script_code = include_str!("../masm/scripts/mapping_example_script.masm"); - // Compile the transaction script with the library. + // Compile the transaction script with the account code linked as a + // module on the same `CodeBuilder` chain (avoids the source-span + // mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("miden_by_example::mapping_example_contract", account_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/docs/src/rust-client/network_transactions_tutorial.md b/docs/src/rust-client/network_transactions_tutorial.md index af58f456..dccf3996 100644 --- a/docs/src/rust-client/network_transactions_tutorial.md +++ b/docs/src/rust-client/network_transactions_tutorial.md @@ -143,14 +143,17 @@ Before deploying the network account and creating network notes, we need to set Copy and paste the following code into your `src/main.rs` file: ```rust no_run -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; -use miden_client::account::component::BasicWallet; use miden_client::{ + account::{ + component::{AccountComponentMetadata, BasicWallet}, AccountBuilder, AccountComponent, + AccountStorageMode, AccountType, StorageSlot, StorageSlotName, + }, address::NetworkId, - auth::AuthSecretKey, - crypto::FeltRng, + auth::{self, AuthSchemeId, AuthSecretKey, AuthSingleSig}, builder::ClientBuilder, + crypto::FeltRng, keystore::{FilesystemKeyStore, Keystore}, note::{ NetworkAccountTarget, Note, NoteAssets, NoteError, NoteExecutionHint, NoteMetadata, @@ -162,23 +165,6 @@ use miden_client::{ Client, ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::auth::{self, AuthSchemeId, AuthSingleSig}; -use miden_client::transaction::TransactionKernel; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, - AccountType, StorageSlot, StorageSlotName, - }, - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Library, - Module, - ModuleKind, - Path as AssemblyPath, - }, -}; use rand::RngCore; use tokio::time::{sleep, Duration}; @@ -214,22 +200,6 @@ async fn wait_for_tx( Ok(()) } -/// Creates a Miden library from the provided account code and library path. -fn create_library( - account_code: String, - library_path: &str, -) -> Result, Box> { - let assembler: Assembler = TransactionKernel::assembler(); - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - account_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client @@ -238,10 +208,10 @@ async fn main() -> Result<(), Box> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -303,14 +273,17 @@ Add this code to your `main()` function: // ------------------------------------------------------------------------- println!("\n[STEP 2] Creating a network counter smart contract"); -let counter_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); +// `include_str!` resolves at compile time relative to this source file, +// so the binary is independent of the working directory it is run from. +let counter_code = include_str!("../masm/accounts/counter.masm"); // Create the network counter smart contract account // First, compile the MASM code into an account component let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); -let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code) +let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code) .unwrap(); let counter_component = AccountComponent::new( component_code, @@ -354,17 +327,15 @@ Add this code to your `main()` function: // ------------------------------------------------------------------------- println!("\n[STEP 3] Deploy network counter smart contract"); -let script_code = fs::read_to_string(Path::new("../masm/scripts/counter_script.masm")).unwrap(); - -let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); -let library_path = "external_contract::counter_contract"; - -let library = create_library(account_code, library_path).unwrap(); +let script_code = include_str!("../masm/scripts/counter_script.masm"); +// Link the counter contract code into the same `CodeBuilder` chain that +// compiles the script, so the assembler shares the client's persisted +// source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_tx_script(&script_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_tx_script(script_code)?; let tx_increment_request = TransactionRequestBuilder::new() .custom_script(tx_script) @@ -399,22 +370,18 @@ Add this code to your `main()` function: // ------------------------------------------------------------------------- println!("\n[STEP 4] Creating a network note for network counter contract"); -let network_note_code = - fs::read_to_string(Path::new("../masm/notes/network_increment_note.masm")).unwrap(); -let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); - -let library_path = "external_contract::counter_contract"; -let library = create_library(account_code, library_path).unwrap(); +let network_note_code = include_str!("../masm/notes/network_increment_note.masm"); // Create and submit the network note that will increment the counter // Generate a random serial number for the note let serial_num = client.rng().draw_word(); -// Compile the note script with the counter contract library +// Compile the note script with the counter contract code linked as a +// module on the same `CodeBuilder` chain (avoids miden-vm#2778). let note_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_note_script(&network_note_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_note_script(network_note_code)?; // Create note recipient with empty storage let note_storage = NoteStorage::new([].to_vec())?; @@ -496,14 +463,17 @@ This step creates a public note that the network operator can consume to execute Your complete `main()` function should look like this: ```rust no_run -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; -use miden_client::account::component::BasicWallet; use miden_client::{ + account::{ + component::{AccountComponentMetadata, BasicWallet}, AccountBuilder, AccountComponent, + AccountStorageMode, AccountType, StorageSlot, StorageSlotName, + }, address::NetworkId, - auth::AuthSecretKey, - crypto::FeltRng, + auth::{self, AuthSchemeId, AuthSecretKey, AuthSingleSig}, builder::ClientBuilder, + crypto::FeltRng, keystore::{FilesystemKeyStore, Keystore}, note::{ NetworkAccountTarget, Note, NoteAssets, NoteError, NoteExecutionHint, NoteMetadata, @@ -515,23 +485,6 @@ use miden_client::{ Client, ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::auth::{self, AuthSchemeId, AuthSingleSig}; -use miden_client::transaction::TransactionKernel; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, - AccountType, StorageSlot, StorageSlotName, - }, - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Library, - Module, - ModuleKind, - Path as AssemblyPath, - }, -}; use rand::RngCore; use tokio::time::{sleep, Duration}; @@ -567,22 +520,6 @@ async fn wait_for_tx( Ok(()) } -/// Creates a Miden library from the provided account code and library path. -fn create_library( - account_code: String, - library_path: &str, -) -> Result, Box> { - let assembler: Assembler = TransactionKernel::assembler(); - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - account_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client @@ -591,10 +528,10 @@ async fn main() -> Result<(), Box> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -643,14 +580,17 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Creating a network counter smart contract"); - let counter_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let counter_code = include_str!("../masm/accounts/counter.masm"); // Create the network counter smart contract account // First, compile the MASM code into an account component let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); - let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code) + let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code) .unwrap(); let counter_component = AccountComponent::new( component_code, @@ -684,17 +624,15 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Deploy network counter smart contract"); - let script_code = fs::read_to_string(Path::new("../masm/scripts/counter_script.masm")).unwrap(); - - let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); - let library_path = "external_contract::counter_contract"; - - let library = create_library(account_code, library_path).unwrap(); + let script_code = include_str!("../masm/scripts/counter_script.masm"); + // Link the counter contract code into the same `CodeBuilder` chain that + // compiles the script, so the assembler shares the client's persisted + // source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_tx_script(&script_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_tx_script(script_code)?; let tx_increment_request = TransactionRequestBuilder::new() .custom_script(tx_script) @@ -719,22 +657,18 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 4] Creating a network note for network counter contract"); - let network_note_code = - fs::read_to_string(Path::new("../masm/notes/network_increment_note.masm")).unwrap(); - let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); - - let library_path = "external_contract::counter_contract"; - let library = create_library(account_code, library_path).unwrap(); + let network_note_code = include_str!("../masm/notes/network_increment_note.masm"); // Create and submit the network note that will increment the counter // Generate a random serial number for the note let serial_num = client.rng().draw_word(); - // Compile the note script with the counter contract library + // Compile the note script with the counter contract code linked as a + // module on the same `CodeBuilder` chain (avoids miden-vm#2778). let note_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_note_script(&network_note_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_note_script(network_note_code)?; // Create note recipient with empty inputs let note_storage = NoteStorage::new([].to_vec())?; diff --git a/docs/src/rust-client/oracle_tutorial.md b/docs/src/rust-client/oracle_tutorial.md index 9b649848..3ffe6b4d 100644 --- a/docs/src/rust-client/oracle_tutorial.md +++ b/docs/src/rust-client/oracle_tutorial.md @@ -41,10 +41,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ### Step 1: Set up your `src/main.rs` file @@ -53,34 +50,24 @@ Copy and paste the following code into your `src/main.rs` file: ```rust no_run use miden_client::{ - assembly::{ - Assembler, - CodeBuilder, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, + account::{ + component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, + AccountStorageMode, AccountType, StorageMapKey, StorageSlot, StorageSlotName, + StorageSlotType, }, + auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{ - domain::account::{AccountStorageRequirements, StorageMapKey}, + domain::account::AccountStorageRequirements, Endpoint, GrpcClient, }, transaction::{ForeignAccount, TransactionRequestBuilder}, - Client, ClientError, + Client, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -use miden_client::{auth::NoAuth, transaction::TransactionKernel}; -use miden_client::{ - account::{ - component::AccountComponentMetadata, AccountComponent, AccountId, AccountStorageMode, - AccountType, StorageSlot, StorageSlotName, StorageSlotType, - }, - Felt, Word, ZERO, -}; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; /// Import the oracle + its publishers and return the ForeignAccount list /// Due to Pragma's decentralized oracle architecture, we need to get the @@ -169,21 +156,6 @@ pub async fn get_oracle_foreign_accounts( Ok(foreign_accounts) } -fn create_library( - assembler: Assembler, - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- @@ -193,10 +165,10 @@ async fn main() -> Result<(), ClientError> { let timeout_ms = 10_000; let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -211,7 +183,11 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Get all foreign accounts for oracle data // ------------------------------------------------------------------------- - // The oracle account ID must be supplied as a CLI argument. + // The Pragma oracle bech32 ID is read from the first CLI argument. + // Live IDs rotate per testnet iteration; the canonical source for the + // current testnet oracle is the `astraly-labs/pragma-miden` README: + // https://github.com/astraly-labs/pragma-miden#deployments + // Run as: `cargo run --release --bin oracle_data_query -- ` let oracle_bech32 = std::env::args() .nth(1) .expect("Usage: oracle_data_query "); @@ -229,13 +205,15 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Create Oracle Reader contract // ------------------------------------------------------------------------- - let contract_code = - fs::read_to_string(Path::new("../masm/accounts/oracle_reader.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let contract_code = include_str!("../masm/accounts/oracle_reader.masm"); let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); - let contract_component_code = CodeBuilder::new() - .compile_component_code("external_contract::oracle_reader", &contract_code) + let contract_component_code = client + .code_builder() + .compile_component_code("external_contract::oracle_reader", contract_code) .unwrap(); let contract_component = AccountComponent::new( contract_component_code, @@ -263,19 +241,17 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Build the script that calls our `get_price` procedure // ------------------------------------------------------------------------- - let script_path = Path::new("../masm/scripts/oracle_reader_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - let assembler = TransactionKernel::assembler(); - let library_path = "external_contract::oracle_reader"; - let account_component_lib = - create_library(assembler.clone(), library_path, &contract_code).unwrap(); + let script_code = include_str!("../masm/scripts/oracle_reader_script.masm"); + // Link the oracle reader contract code into the same `CodeBuilder` chain + // that compiles the script, so the assembler shares the client's + // persisted source manager (avoids the source-span mismatch from + // miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::oracle_reader", contract_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); let tx_increment_request = TransactionRequestBuilder::new() @@ -302,10 +278,10 @@ async fn main() -> Result<(), ClientError> { _Don't run this code just yet, we still need to create our smart contract that queries the oracle_ -In the code above, we specified the Pragma oracle account id `0x4f67e78643022e00000220d8997e33` and the BTC/USD pair `120195681`. The `get_oracle_foreign_accounts` function returns all of the `ForeignAccounts` that you will need to execute the transaction to get the price data from the oracle. Since Pragma's oracle depends on multiple publishers, this function queries all of the publisher account ids required to make a successful FPI call. +The oracle bech32 ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is `120195681`. The `get_oracle_foreign_accounts` function returns all of the `ForeignAccounts` that you will need to execute the transaction to get the price data from the oracle. Since Pragma's oracle depends on multiple publishers, this function queries all of the publisher account ids required to make a successful FPI call. :::note -The oracle account ID, procedure hash, and trading pair ID used in this tutorial reference Pragma's testnet deployment. These values are maintained by Pragma and may change if they redeploy their oracle. For the latest values, check the [Pragma Miden repository](https://github.com/astraly-labs/pragma-miden). +The oracle account ID, procedure hash, and trading pair ID used in this tutorial reference Pragma's testnet deployment. Live IDs rotate per testnet iteration; the canonical source for the current testnet oracle is the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments). ::: ## Step 2: Build the price reader smart contract and script @@ -388,10 +364,10 @@ end ## Step 3: Run the program -Run the following command to execute src/main.rs: +This tutorial requires a live Pragma oracle deployment. Get the current testnet oracle bech32 ID from the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments) and pass it as a CLI argument: ``` -cargo run --release +cargo run --release -- ``` The output of our program will look something like this: diff --git a/docs/src/rust-client/public_account_interaction_tutorial.md b/docs/src/rust-client/public_account_interaction_tutorial.md index 1bdc4b1d..f80bfb4e 100644 --- a/docs/src/rust-client/public_account_interaction_tutorial.md +++ b/docs/src/rust-client/public_account_interaction_tutorial.md @@ -121,18 +121,10 @@ end Copy and paste the following code into your `src/main.rs` file: ```rust no_run -use miden_client::transaction::TransactionKernel; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ - account::AccountId, - assembly::{ - Assembler, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, - }, + account::{AccountId, StorageSlotName}, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, @@ -141,21 +133,6 @@ use miden_client::{ }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - assembler: Assembler, - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -164,10 +141,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -241,26 +218,19 @@ Add the following code snippet to the end of your `src/main.rs` function: // ------------------------------------------------------------------------- println!("\n[STEP 2] Call the increment_count procedure in the counter contract"); -// Load the MASM script referencing the increment procedure -let script_path = Path::new("../masm/scripts/counter_script.masm"); -let script_code = fs::read_to_string(script_path).unwrap(); - -let counter_path = Path::new("../masm/accounts/counter.masm"); -let counter_code = fs::read_to_string(counter_path).unwrap(); - -let assembler = TransactionKernel::assembler(); -let account_component_lib = create_library( - assembler.clone(), - "external_contract::counter_contract", - &counter_code, -) -.unwrap(); +// Load the MASM sources at compile time so the binary is independent of +// the working directory it is run from. +let script_code = include_str!("../masm/scripts/counter_script.masm"); +let counter_code = include_str!("../masm/accounts/counter.masm"); +// Compile the script against the counter contract code via a single +// `CodeBuilder` chain so the assembler shares the client's persisted +// source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script @@ -302,18 +272,10 @@ println!( The final `src/main.rs` file should look like this: ```rust no_run -use miden_client::transaction::TransactionKernel; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ - account::AccountId, - assembly::{ - Assembler, - DefaultSourceManager, - Module, - ModuleKind, - Path as AssemblyPath, - }, + account::{AccountId, StorageSlotName}, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, @@ -322,21 +284,6 @@ use miden_client::{ }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - assembler: Assembler, - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager.clone(), - )?; - let library = assembler.clone().assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -345,10 +292,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -390,26 +337,19 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Call the increment_count procedure in the counter contract"); - // Load the MASM script referencing the increment procedure - let script_path = Path::new("../masm/scripts/counter_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - let counter_path = Path::new("../masm/accounts/counter.masm"); - let counter_code = fs::read_to_string(counter_path).unwrap(); - - let assembler = TransactionKernel::assembler(); - let account_component_lib = create_library( - assembler.clone(), - "external_contract::counter_contract", - &counter_code, - ) - .unwrap(); + // Load the MASM sources at compile time so the binary is independent of + // the working directory it is run from. + let script_code = include_str!("../masm/scripts/counter_script.masm"); + let counter_code = include_str!("../masm/accounts/counter.masm"); + // Compile the script against the counter contract code via a single + // `CodeBuilder` chain so the assembler shares the client's persisted + // source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/rust-client/Cargo.lock b/rust-client/Cargo.lock index ce9c7be4..c9e7d9a4 100644 --- a/rust-client/Cargo.lock +++ b/rust-client/Cargo.lock @@ -3328,9 +3328,6 @@ dependencies = [ "miden-client-sqlite-store", "miden-protocol", "rand 0.9.4", - "rand_chacha", - "serde", - "serde_json", "tokio", ] diff --git a/rust-client/Cargo.toml b/rust-client/Cargo.toml index 25a57283..39339e55 100644 --- a/rust-client/Cargo.toml +++ b/rust-client/Cargo.toml @@ -8,7 +8,4 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.48", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" diff --git a/rust-client/src/bin/counter_contract_deploy.rs b/rust-client/src/bin/counter_contract_deploy.rs index 0638e47b..0ead348b 100644 --- a/rust-client/src/bin/counter_contract_deploy.rs +++ b/rust-client/src/bin/counter_contract_deploy.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::{ @@ -7,34 +7,15 @@ use miden_client::{ AccountStorageMode, AccountType, StorageSlot, StorageSlotName, }, address::NetworkId, - assembly::{ - CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind, - Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, - transaction::{TransactionKernel, TransactionRequestBuilder}, + transaction::TransactionRequestBuilder, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -43,10 +24,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -64,15 +45,20 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating counter contract."); - // Load the MASM file for the counter contract - let counter_path = Path::new("../masm/accounts/counter.masm"); - let counter_code = fs::read_to_string(counter_path).unwrap(); + // Load the MASM file for the counter contract. `include_str!` resolves at + // compile time relative to this source file, so the binary is independent + // of the working directory it is run from. + let counter_code = include_str!("../../../masm/accounts/counter.masm"); - // Compile the account code into `AccountComponent` with one storage slot + // Compile the account code into `AccountComponent` with one storage slot. + // Using `client.code_builder()` makes the assembler share the client's + // persisted source manager, which keeps debug spans coherent for any + // libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); - let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code) + let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code) .unwrap(); let counter_component = AccountComponent::new( component_code, @@ -112,21 +98,17 @@ async fn main() -> Result<(), ClientError> { println!("\n[STEP 2] Call Counter Contract With Script"); // Load the MASM script referencing the increment procedure - let script_path = Path::new("../masm/scripts/counter_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - // Create a library from the counter contract code - let account_component_lib = create_library( - "external_contract::counter_contract", - &counter_code, - ) - .unwrap(); + let script_code = include_str!("../../../masm/scripts/counter_script.masm"); + // Compile the script with the counter contract code linked as a dynamic + // module on the same `CodeBuilder`. This shares the client's source + // manager between parsing and assembly, which is what miden-vm#2778 + // requires to avoid panics when debug spans are reported. let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/rust-client/src/bin/counter_contract_fpi.rs b/rust-client/src/bin/counter_contract_fpi.rs index 0bcb9c1e..4c2b09b6 100644 --- a/rust-client/src/bin/counter_contract_fpi.rs +++ b/rust-client/src/bin/counter_contract_fpi.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::{fs, path::Path, sync::Arc, time::Duration}; +use std::{path::PathBuf, sync::Arc, time::Duration}; use tokio::time::sleep; use miden_client::{ @@ -7,34 +7,15 @@ use miden_client::{ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, AccountStorageMode, AccountType, StorageSlot, StorageSlotName, }, - assembly::{ - CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind, - Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{domain::account::AccountStorageRequirements, Endpoint, GrpcClient}, - transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder}, + transaction::{ForeignAccount, TransactionRequestBuilder}, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -43,10 +24,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -64,15 +45,17 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Creating count reader contract."); - let count_reader_path = Path::new("../masm/accounts/count_reader.masm"); - let count_reader_code = fs::read_to_string(count_reader_path).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let count_reader_code = include_str!("../../../masm/accounts/count_reader.masm"); let count_reader_slot_name = StorageSlotName::new("miden::tutorials::count_reader").expect("valid slot name"); - let count_reader_component_code = CodeBuilder::new() + let count_reader_component_code = client + .code_builder() .compile_component_code( "external_contract::count_reader_contract", - &count_reader_code, + count_reader_code, ) .unwrap(); let count_reader_component = AccountComponent::new( @@ -141,13 +124,13 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Call counter contract with FPI from count reader contract"); - let counter_contract_path = Path::new("../masm/accounts/counter.masm"); - let counter_contract_code = fs::read_to_string(counter_contract_path).unwrap(); + let counter_contract_code = include_str!("../../../masm/accounts/counter.masm"); // Compile the counter as a component (same path as the deploy binary) to get // the correct procedure root that matches the on-chain MAST. - let counter_component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_contract_code) + let counter_component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_contract_code) .unwrap(); let counter_component = AccountComponent::new( counter_component_code, @@ -167,9 +150,7 @@ async fn main() -> Result<(), ClientError> { println!("counter id prefix: {:?}", counter_contract_id.prefix()); println!("counter id suffix: {:?}", counter_contract_id.suffix()); - let script_path = Path::new("../masm/scripts/reader_script.masm"); - let script_code_original = fs::read_to_string(script_path).unwrap(); - let script_code = script_code_original + let script_code = include_str!("../../../masm/scripts/reader_script.masm") .replace("{get_count_proc_hash}", &get_count_hash) .replace( "{account_id_suffix}", @@ -180,17 +161,15 @@ async fn main() -> Result<(), ClientError> { &u64::from(counter_contract_id.prefix()).to_string(), ); - let account_component_lib = create_library( - "external_contract::count_reader_contract", - &count_reader_code, - ) - .unwrap(); - + // Link the count reader contract code into the same `CodeBuilder` chain + // that compiles the script, so the assembler shares the client's + // persisted source manager (avoids the source-span mismatch from + // miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::count_reader_contract", count_reader_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code.as_str()) .unwrap(); let foreign_account = diff --git a/rust-client/src/bin/counter_contract_increment.rs b/rust-client/src/bin/counter_contract_increment.rs index cad179b8..6f0415e4 100644 --- a/rust-client/src/bin/counter_contract_increment.rs +++ b/rust-client/src/bin/counter_contract_increment.rs @@ -1,33 +1,15 @@ -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::{AccountId, StorageSlotName}, - assembly::{ - DefaultSourceManager, Library, Module, ModuleKind, Path as AssemblyPath, - }, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, - transaction::{TransactionKernel, TransactionRequestBuilder}, + transaction::TransactionRequestBuilder, ClientError, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -36,10 +18,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -81,24 +63,19 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Call the increment_count procedure in the counter contract"); - // Load the MASM script referencing the increment procedure - let script_path = Path::new("../masm/scripts/counter_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - let counter_path = Path::new("../masm/accounts/counter.masm"); - let counter_code = fs::read_to_string(counter_path).unwrap(); - - let account_component_lib = create_library( - "external_contract::counter_contract", - &counter_code, - ) - .unwrap(); + // Load the MASM sources at compile time so the binary is independent of + // the working directory it is run from. + let script_code = include_str!("../../../masm/scripts/counter_script.masm"); + let counter_code = include_str!("../../../masm/accounts/counter.masm"); + // Compile the script against the counter contract code via a single + // `CodeBuilder` chain so the assembler shares the client's persisted + // source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::counter_contract", counter_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/rust-client/src/bin/create_mint_consume_send.rs b/rust-client/src/bin/create_mint_consume_send.rs index 4bb7ed5d..25f7f3d1 100644 --- a/rust-client/src/bin/create_mint_consume_send.rs +++ b/rust-client/src/bin/create_mint_consume_send.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::Duration; use miden_client::{ @@ -28,10 +28,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/rust-client/src/bin/delegated_prover.rs b/rust-client/src/bin/delegated_prover.rs index 96d8b758..535565b3 100644 --- a/rust-client/src/bin/delegated_prover.rs +++ b/rust-client/src/bin/delegated_prover.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::{component::BasicWallet, AccountBuilder, AccountStorageMode, AccountType}, @@ -22,10 +22,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/rust-client/src/bin/hash_preimage_note.rs b/rust-client/src/bin/hash_preimage_note.rs index 5a970ba5..726d7534 100644 --- a/rust-client/src/bin/hash_preimage_note.rs +++ b/rust-client/src/bin/hash_preimage_note.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration}; use miden_client::{ @@ -113,10 +113,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -202,7 +202,9 @@ async fn main() -> Result<(), ClientError> { let digest = Hasher::hash_elements(&secret_vals); println!("digest: {:?}", digest); - let code = fs::read_to_string(Path::new("../masm/notes/hash_preimage_note.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let code = include_str!("../../../masm/notes/hash_preimage_note.masm"); let serial_num = client.rng().draw_word(); let note_script = client.code_builder().compile_note_script(code).unwrap(); diff --git a/rust-client/src/bin/mapping_example.rs b/rust-client/src/bin/mapping_example.rs index bf703450..9f9626b5 100644 --- a/rust-client/src/bin/mapping_example.rs +++ b/rust-client/src/bin/mapping_example.rs @@ -1,39 +1,20 @@ use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::{ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode, AccountType, StorageMap, StorageSlot, StorageSlotName, }, - assembly::{ - CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind, - Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, rpc::{Endpoint, GrpcClient}, - transaction::{TransactionKernel, TransactionRequestBuilder}, + transaction::TransactionRequestBuilder, ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // Initialize client @@ -42,10 +23,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -63,9 +44,9 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 1] Deploy a smart contract with a mapping"); - // Load the MASM file for the counter contract - let file_path = Path::new("../masm/accounts/mapping_example_contract.masm"); - let account_code = fs::read_to_string(file_path).unwrap(); + // Load the MASM file for the counter contract. `include_str!` resolves at + // compile time relative to this source file. + let account_code = include_str!("../../../masm/accounts/mapping_example_contract.masm"); // Using an empty storage value in slot 0 since this is usually reserved // for the account pub_key and metadata @@ -80,8 +61,9 @@ async fn main() -> Result<(), ClientError> { let storage_slot_map = StorageSlot::with_map(map_slot_name.clone(), storage_map.clone()); // Compile the account code into `AccountComponent` with one storage slot - let component_code = CodeBuilder::new() - .compile_component_code("miden_by_example::mapping_example_contract", &account_code) + let component_code = client + .code_builder() + .compile_component_code("miden_by_example::mapping_example_contract", account_code) .unwrap(); let mapping_contract_component = AccountComponent::new( component_code, @@ -113,22 +95,16 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Call Mapping Contract With Script"); - let script_code = - fs::read_to_string(Path::new("../masm/scripts/mapping_example_script.masm")).unwrap(); - - // Create the library from the account source code using the helper function. - let account_component_lib = create_library( - "miden_by_example::mapping_example_contract", - &account_code, - ) - .unwrap(); + let script_code = include_str!("../../../masm/scripts/mapping_example_script.masm"); - // Compile the transaction script with the library. + // Compile the transaction script with the account code linked as a + // module on the same `CodeBuilder` chain (avoids the source-span + // mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("miden_by_example::mapping_example_contract", account_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); // Build a transaction request with the custom script diff --git a/rust-client/src/bin/network_notes_counter_contract.rs b/rust-client/src/bin/network_notes_counter_contract.rs index ba61159a..40cd41b9 100644 --- a/rust-client/src/bin/network_notes_counter_contract.rs +++ b/rust-client/src/bin/network_notes_counter_contract.rs @@ -1,4 +1,4 @@ -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::{ @@ -6,10 +6,6 @@ use miden_client::{ AccountStorageMode, AccountType, StorageSlot, StorageSlotName, }, address::NetworkId, - assembly::{ - CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind, - Path as AssemblyPath, - }, auth::{self, AuthSchemeId, AuthSecretKey, AuthSingleSig}, builder::ClientBuilder, crypto::FeltRng, @@ -21,7 +17,7 @@ use miden_client::{ rpc::{Endpoint, GrpcClient}, store::TransactionFilter, transaction::{ - TransactionId, TransactionKernel, TransactionRequestBuilder, TransactionStatus, + TransactionId, TransactionRequestBuilder, TransactionStatus, }, Client, ClientError, Felt, Word, }; @@ -61,22 +57,6 @@ async fn wait_for_tx( Ok(()) } -/// Creates a Miden library from the provided account code and library path. -fn create_library( - account_code: String, - library_path: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - account_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client @@ -85,10 +65,10 @@ async fn main() -> Result<(), Box> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -137,14 +117,17 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 2] Creating a network counter smart contract"); - let counter_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let counter_code = include_str!("../../../masm/accounts/counter.masm"); // Create the network counter smart contract account // First, compile the MASM code into an account component let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); - let component_code = CodeBuilder::new() - .compile_component_code("external_contract::counter_contract", &counter_code)?; + let component_code = client + .code_builder() + .compile_component_code("external_contract::counter_contract", counter_code)?; let counter_component = AccountComponent::new( component_code, vec![StorageSlot::with_value( @@ -179,17 +162,15 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Deploy network counter smart contract"); - let script_code = fs::read_to_string(Path::new("../masm/scripts/counter_script.masm")).unwrap(); - - let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); - let library_path = "external_contract::counter_contract"; - - let library = create_library(account_code, library_path).unwrap(); + let script_code = include_str!("../../../masm/scripts/counter_script.masm"); + // Link the counter contract code into the same `CodeBuilder` chain that + // compiles the script, so the assembler shares the client's persisted + // source manager (avoids the source-span mismatch from miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_tx_script(&script_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_tx_script(script_code)?; let tx_increment_request = TransactionRequestBuilder::new() .custom_script(tx_script) @@ -214,22 +195,18 @@ async fn main() -> Result<(), Box> { // ------------------------------------------------------------------------- println!("\n[STEP 4] Creating a network note for network counter contract"); - let network_note_code = - fs::read_to_string(Path::new("../masm/notes/network_increment_note.masm")).unwrap(); - let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap(); - - let library_path = "external_contract::counter_contract"; - let library = create_library(account_code, library_path).unwrap(); + let network_note_code = include_str!("../../../masm/notes/network_increment_note.masm"); // Create and submit the network note that will increment the counter // Generate a random serial number for the note let serial_num = client.rng().draw_word(); - // Compile the note script with the counter contract library + // Compile the note script with the counter contract code linked as a + // module on the same `CodeBuilder` chain (avoids miden-vm#2778). let note_script = client .code_builder() - .with_dynamically_linked_library(&library)? - .compile_note_script(&network_note_code)?; + .with_linked_module("external_contract::counter_contract", counter_code)? + .compile_note_script(network_note_code)?; // Create note recipient with empty inputs let note_storage = NoteStorage::new([].to_vec())?; diff --git a/rust-client/src/bin/note_creation_in_masm.rs b/rust-client/src/bin/note_creation_in_masm.rs index 5dc30263..aec47edd 100644 --- a/rust-client/src/bin/note_creation_in_masm.rs +++ b/rust-client/src/bin/note_creation_in_masm.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration}; use miden_client::{ @@ -103,10 +103,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -186,13 +186,15 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- println!("\n[STEP 3] Create iterative output note"); - let code = fs::read_to_string(Path::new("../masm/notes/iterative_output_note.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let code = include_str!("../../../masm/notes/iterative_output_note.masm"); let serial_num = client.rng().draw_word(); // Create note metadata and tag let tag = NoteTag::new(0); let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public).with_tag(tag); - let note_script = client.code_builder().compile_note_script(&code).unwrap(); + let note_script = client.code_builder().compile_note_script(code).unwrap(); let note_storage = NoteStorage::new(vec![ alice_account.id().prefix().as_felt(), alice_account.id().suffix(), diff --git a/rust-client/src/bin/oracle_data_query.rs b/rust-client/src/bin/oracle_data_query.rs index 9c3b7a94..5387691f 100644 --- a/rust-client/src/bin/oracle_data_query.rs +++ b/rust-client/src/bin/oracle_data_query.rs @@ -4,9 +4,6 @@ use miden_client::{ AccountStorageMode, AccountType, StorageMapKey, StorageSlot, StorageSlotName, StorageSlotType, }, - assembly::{ - CodeBuilder, DefaultSourceManager, Module, ModuleKind, Path as AssemblyPath, - }, auth::NoAuth, builder::ClientBuilder, keystore::FilesystemKeyStore, @@ -14,12 +11,12 @@ use miden_client::{ domain::account::AccountStorageRequirements, Endpoint, GrpcClient, }, - transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder}, + transaction::{ForeignAccount, TransactionRequestBuilder}, Client, ClientError, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; use rand::RngCore; -use std::{fs, path::Path, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; /// Import the oracle + its publishers and return the ForeignAccount list /// Due to Pragma's decentralized oracle architecture, we need to get the @@ -108,21 +105,6 @@ pub async fn get_oracle_foreign_accounts( Ok(foreign_accounts) } -fn create_library( - library_path: &str, - source_code: &str, -) -> Result, Box> { - let source_manager = Arc::new(DefaultSourceManager::default()); - let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone()); - let module = Module::parser(ModuleKind::Library).parse_str( - AssemblyPath::new(library_path), - source_code, - source_manager, - )?; - let library = assembler.assemble_library([module])?; - Ok(library) -} - #[tokio::main] async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- @@ -132,10 +114,10 @@ async fn main() -> Result<(), ClientError> { let timeout_ms = 10_000; let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -150,6 +132,11 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Get all foreign accounts for oracle data // ------------------------------------------------------------------------- + // The Pragma oracle bech32 ID is read from the first CLI argument. + // Live IDs rotate per testnet iteration; the canonical source for the + // current testnet oracle is the `astraly-labs/pragma-miden` README: + // https://github.com/astraly-labs/pragma-miden#deployments + // Run as: `cargo run --release --bin oracle_data_query -- ` let oracle_bech32 = std::env::args() .nth(1) .expect("Usage: oracle_data_query "); @@ -167,13 +154,15 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Create Oracle Reader contract // ------------------------------------------------------------------------- - let contract_code = - fs::read_to_string(Path::new("../masm/accounts/oracle_reader.masm")).unwrap(); + // `include_str!` resolves at compile time relative to this source file, + // so the binary is independent of the working directory it is run from. + let contract_code = include_str!("../../../masm/accounts/oracle_reader.masm"); let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); - let contract_component_code = CodeBuilder::new() - .compile_component_code("external_contract::oracle_reader", &contract_code) + let contract_component_code = client + .code_builder() + .compile_component_code("external_contract::oracle_reader", contract_code) .unwrap(); let contract_component = AccountComponent::new( contract_component_code, @@ -204,18 +193,17 @@ async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- // Build the script that calls our `get_price` procedure // ------------------------------------------------------------------------- - let script_path = Path::new("../masm/scripts/oracle_reader_script.masm"); - let script_code = fs::read_to_string(script_path).unwrap(); - - let library_path = "external_contract::oracle_reader"; - let account_component_lib = - create_library(library_path, &contract_code).unwrap(); + let script_code = include_str!("../../../masm/scripts/oracle_reader_script.masm"); + // Link the oracle reader contract code into the same `CodeBuilder` chain + // that compiles the script, so the assembler shares the client's + // persisted source manager (avoids the source-span mismatch from + // miden-vm#2778). let tx_script = client .code_builder() - .with_dynamically_linked_library(&account_component_lib) + .with_linked_module("external_contract::oracle_reader", contract_code) .unwrap() - .compile_tx_script(&script_code) + .compile_tx_script(script_code) .unwrap(); let tx_increment_request = TransactionRequestBuilder::new() diff --git a/rust-client/src/bin/unauthenticated_note_transfer.rs b/rust-client/src/bin/unauthenticated_note_transfer.rs index fcaacbbd..9494f563 100644 --- a/rust-client/src/bin/unauthenticated_note_transfer.rs +++ b/rust-client/src/bin/unauthenticated_note_transfer.rs @@ -1,5 +1,5 @@ use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration, Instant}; use miden_client::{ @@ -61,10 +61,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) From 043be4334f9ef73b40810af0a84743259db5f03a Mon Sep 17 00:00:00 2001 From: keinberger Date: Tue, 28 Apr 2026 15:32:26 +0300 Subject: [PATCH 2/5] fix: revise rust tutorial issue fixes --- docs/Cargo.lock | 333 ++++++++++++++--- docs/Cargo.toml | 3 - .../rust-client/counter_contract_tutorial.md | 3 - .../src/rust-client/create_deploy_tutorial.md | 15 +- .../creating_notes_in_masm_tutorial.md | 11 +- docs/src/rust-client/custom_note_how_to.md | 5 +- .../rust-client/delegated_proving_tutorial.md | 9 +- .../mint_consume_create_tutorial.md | 6 +- .../network_transactions_tutorial.md | 10 +- docs/src/rust-client/oracle_tutorial.md | 241 ++++++------ .../public_account_interaction_tutorial.md | 3 - .../unauthenticated_note_how_to.md | 6 +- masm/accounts/oracle_reader.masm | 29 +- masm/notes/hash_preimage_note.masm | 3 +- masm/notes/iterative_output_note.masm | 6 +- masm/notes/network_increment_note.masm | 5 +- rust-client/Cargo.lock | 345 +++++++++++++++--- rust-client/src/bin/oracle_data_query.rs | 206 ++++++----- 18 files changed, 866 insertions(+), 373 deletions(-) diff --git a/docs/Cargo.lock b/docs/Cargo.lock index 83b87d8e..e5f29d65 100644 --- a/docs/Cargo.lock +++ b/docs/Cargo.lock @@ -422,6 +422,45 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.115", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "colorchoice" version = "1.0.4" @@ -1221,7 +1260,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -1556,7 +1595,7 @@ dependencies = [ "miden-utils-sync", "primitive-types", "regex", - "thiserror", + "thiserror 2.0.17", "walkdir", ] @@ -1569,7 +1608,7 @@ dependencies = [ "miden-core", "miden-crypto", "miden-utils-indexing", - "thiserror", + "thiserror 2.0.17", "tracing", ] @@ -1587,7 +1626,7 @@ dependencies = [ "miden-package-registry", "miden-project", "smallvec", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1612,7 +1651,7 @@ dependencies = [ "semver 1.0.27", "serde", "smallvec", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1622,14 +1661,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde56bcea3cebe307786a856e204d84e7987c318e5a2909bcbb655d16286ce31" dependencies = [ "miden-protocol", - "thiserror", + "thiserror 2.0.17", ] [[package]] name = "miden-client" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49957f76717961769c911237113bb9c1841c3b13970c15ca09a0ae639c33fc45" +checksum = "15007f2cf4e80316a8141665b43f454e77ce0dfd2ac0307ce6cdf7a5d552d58b" dependencies = [ "anyhow", "async-trait", @@ -1638,6 +1677,7 @@ dependencies = [ "getrandom 0.3.4", "gloo-timers", "hex", + "miden-debug", "miden-node-proto-build", "miden-note-transport-proto-build", "miden-protocol", @@ -1651,7 +1691,8 @@ dependencies = [ "rand 0.9.2", "serde", "serde_json", - "thiserror", + "tempfile", + "thiserror 2.0.17", "tokio", "tonic", "tonic-health", @@ -1665,9 +1706,9 @@ dependencies = [ [[package]] name = "miden-client-sqlite-store" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab146099a4bf8319f5cac47e110eca7d3e3caa9d2fc354693458f905f4750a7" +checksum = "53a6d9c9bf443b9df440c010eeab6916ab6bc529faed5eb19f84ab7bc21aad59" dependencies = [ "anyhow", "async-trait", @@ -1678,7 +1719,7 @@ dependencies = [ "miden-protocol", "rusqlite", "rusqlite_migration", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -1701,7 +1742,7 @@ dependencies = [ "proptest", "proptest-derive", "serde", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1718,7 +1759,7 @@ dependencies = [ "miden-package-registry", "miden-processor", "miden-utils-sync", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1760,7 +1801,7 @@ dependencies = [ "sha2", "sha3", "subtle", - "thiserror", + "thiserror 2.0.17", "x25519-dalek", ] @@ -1774,6 +1815,77 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "miden-debug" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df04a684eeb96efabc63e2800a01945f7f6e19ffd00d3b49064e85f7e1fac444" +dependencies = [ + "clap", + "futures", + "glob", + "log", + "miden-assembly", + "miden-assembly-syntax", + "miden-core", + "miden-crypto", + "miden-debug-dap", + "miden-debug-engine", + "miden-debug-types", + "miden-mast-package", + "miden-processor", + "miden-protocol", + "miden-thiserror", + "miden-tx", + "num-traits", + "rustc-demangle", + "serde", + "serde_json", + "smallvec", + "socket2 0.5.10", + "tokio", + "tokio-util", + "toml 0.8.23", +] + +[[package]] +name = "miden-debug-dap" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cd41176322df12836bb4deecd4b619f7cf8239ed7b2c4ac1da7b2830e5199c" +dependencies = [ + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "miden-debug-engine" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03dd00bd4dab99dbfdec9fd07009811a7e3b3a988e74a28c6ccb735ac34e138" +dependencies = [ + "clap", + "glob", + "log", + "miden-assembly", + "miden-assembly-syntax", + "miden-core", + "miden-debug-dap", + "miden-debug-types", + "miden-mast-package", + "miden-processor", + "miden-thiserror", + "miden-tx", + "num-traits", + "rustc-demangle", + "serde", + "serde_json", + "smallvec", + "socket2 0.5.10", + "toml 0.8.23", +] + [[package]] name = "miden-debug-types" version = "0.22.1" @@ -1788,8 +1900,8 @@ dependencies = [ "miden-utils-sync", "paste", "serde", - "serde_spanned", - "thiserror", + "serde_spanned 1.1.1", + "thiserror 2.0.17", ] [[package]] @@ -1807,7 +1919,7 @@ dependencies = [ "rand 0.10.0", "serde", "subtle", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1830,7 +1942,7 @@ dependencies = [ "miden-core", "miden-debug-types", "serde", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1853,7 +1965,7 @@ dependencies = [ "strip-ansi-escapes", "syn 2.0.115", "textwrap", - "thiserror", + "thiserror 2.0.17", "trybuild", "unicode-width 0.1.14", ] @@ -1871,9 +1983,9 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.14.4" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9beefe1d90ebeb044b15a8c764eb7031d892f9ae357ac9c8847c1cab3b07045a" +checksum = "6e457758c9a6aa5f70f687f1081833f102572ee1d02baf549625868917495815" dependencies = [ "build-rs", "fs-err", @@ -1906,7 +2018,7 @@ dependencies = [ "pubgrub", "serde", "smallvec", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1923,7 +2035,7 @@ dependencies = [ "miden-utils-indexing", "paste", "rayon", - "thiserror", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -1940,15 +2052,15 @@ dependencies = [ "miden-package-registry", "serde", "serde-untagged", - "thiserror", + "thiserror 2.0.17", "toml 1.1.2+spec-1.1.0", ] [[package]] name = "miden-protocol" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e860cc978d3467297de076e9bd22f0573b82ef73a3d223d6bb957731a45b8164" +checksum = "140b1b3d6b2a3a2bfaeca8b0d2ca15a8ee6a7e0abebbc4f1a2a3a1f1b7f7f58d" dependencies = [ "bech32", "fs-err", @@ -1969,7 +2081,7 @@ dependencies = [ "regex", "semver 1.0.27", "serde", - "thiserror", + "thiserror 2.0.17", "toml 1.1.2+spec-1.1.0", "walkdir", ] @@ -1998,16 +2110,16 @@ dependencies = [ "miden-debug-types", "miden-processor", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", "tracing", ] [[package]] name = "miden-remote-prover-client" -version = "0.14.4" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf7590551de605a7dc6217d81e76c8f00196dbaeb905cef4239c7bc7725fbeb6" +checksum = "5a388883f27bff95127c962669eb5d294d27379ac357d6dac5c77faa81b30def" dependencies = [ "build-rs", "fs-err", @@ -2017,7 +2129,7 @@ dependencies = [ "miden-tx", "miette", "prost", - "thiserror", + "thiserror 2.0.17", "tokio", "tonic", "tonic-prost", @@ -2037,9 +2149,9 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f455a087f41c30636b45ead961d1e66114d2d20661887b307cede05307eeb942" +checksum = "a08e4e4edb289460b1b676a9cdb1727131c2749218dd85a333571d09e1cfa621" dependencies = [ "fs-err", "miden-assembly", @@ -2049,7 +2161,7 @@ dependencies = [ "miden-protocol", "rand 0.9.2", "regex", - "thiserror", + "thiserror 2.0.17", "walkdir", ] @@ -2073,7 +2185,27 @@ dependencies = [ "miden-tx-batch-prover", "rand 0.9.2", "rand_chacha", - "thiserror", + "thiserror 2.0.17", +] + +[[package]] +name = "miden-thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183ff8de338956ecfde3a38573241eb7a6f3d44d73866c210e5629c07fa00253" +dependencies = [ + "miden-thiserror-impl", +] + +[[package]] +name = "miden-thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee4176a0f2e7d29d2a8ee7e60b6deb14ce67a20e94c3e2c7275cdb8804e1862" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", ] [[package]] @@ -2084,9 +2216,6 @@ dependencies = [ "miden-client-sqlite-store", "miden-protocol", "rand 0.9.2", - "rand_chacha", - "serde", - "serde_json", "tokio", ] @@ -2101,7 +2230,7 @@ dependencies = [ "miden-prover", "miden-standards", "miden-verifier", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2146,7 +2275,7 @@ checksum = "c8834e76299686bcce3de1685158aa4cff49b7fa5e0e00a6cc811e8f2cf5775f" dependencies = [ "miden-crypto", "serde", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2172,7 +2301,7 @@ dependencies = [ "miden-core", "miden-crypto", "serde", - "thiserror", + "thiserror 2.0.17", "tracing", ] @@ -2187,7 +2316,7 @@ dependencies = [ "serde", "serde_repr", "smallvec", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2562,7 +2691,7 @@ dependencies = [ "p3-field", "p3-matrix", "p3-util", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2581,7 +2710,7 @@ dependencies = [ "p3-miden-transcript", "p3-util", "rand 0.10.0", - "thiserror", + "thiserror 2.0.17", "tracing", ] @@ -2602,7 +2731,7 @@ dependencies = [ "p3-miden-stateful-hasher", "p3-miden-transcript", "p3-util", - "thiserror", + "thiserror 2.0.17", "tracing", ] @@ -2622,7 +2751,7 @@ dependencies = [ "p3-util", "rand 0.10.0", "serde", - "thiserror", + "thiserror 2.0.17", "tracing", ] @@ -2645,7 +2774,7 @@ dependencies = [ "p3-challenger", "p3-field", "serde", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -3028,7 +3157,7 @@ dependencies = [ "prost-reflect", "prost-types", "protox-parse", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -3040,7 +3169,7 @@ dependencies = [ "logos", "miette", "prost-types", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -3053,7 +3182,7 @@ dependencies = [ "log", "priority-queue", "rustc-hash", - "thiserror", + "thiserror 2.0.17", "version-ranges", ] @@ -3574,6 +3703,15 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -3656,6 +3794,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.1" @@ -3727,6 +3875,12 @@ dependencies = [ "vte", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -3852,13 +4006,33 @@ dependencies = [ "unicode-width 0.2.2", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", ] [[package]] @@ -3900,7 +4074,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2", + "socket2 0.6.1", "tokio-macros", "windows-sys 0.61.2", ] @@ -3951,6 +4125,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", +] + [[package]] name = "toml" version = "0.9.8" @@ -3959,7 +4146,7 @@ checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", - "serde_spanned", + "serde_spanned 1.1.1", "toml_datetime 0.7.3", "toml_parser", "toml_writer", @@ -3974,13 +4161,22 @@ checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned", + "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", "winnow 1.0.1", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.7.3" @@ -3999,6 +4195,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.13", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" @@ -4008,6 +4218,12 @@ dependencies = [ "winnow 1.0.1", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -4033,7 +4249,7 @@ dependencies = [ "percent-encoding", "pin-project", "rustls-native-certs", - "socket2", + "socket2 0.6.1", "sync_wrapper", "tokio", "tokio-rustls", @@ -4112,7 +4328,7 @@ dependencies = [ "httparse", "js-sys", "pin-project", - "thiserror", + "thiserror 2.0.17", "tonic", "tower-service", "wasm-bindgen", @@ -4854,6 +5070,9 @@ name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] [[package]] name = "winnow" diff --git a/docs/Cargo.toml b/docs/Cargo.toml index eeb43898..24b18f66 100644 --- a/docs/Cargo.toml +++ b/docs/Cargo.toml @@ -8,7 +8,4 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.48", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" diff --git a/docs/src/rust-client/counter_contract_tutorial.md b/docs/src/rust-client/counter_contract_tutorial.md index deeb1765..d7da6a6b 100644 --- a/docs/src/rust-client/counter_contract_tutorial.md +++ b/docs/src/rust-client/counter_contract_tutorial.md @@ -41,10 +41,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ### Set up your `src/main.rs` file diff --git a/docs/src/rust-client/create_deploy_tutorial.md b/docs/src/rust-client/create_deploy_tutorial.md index f355e862..517a4f3b 100644 --- a/docs/src/rust-client/create_deploy_tutorial.md +++ b/docs/src/rust-client/create_deploy_tutorial.md @@ -52,10 +52,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ## Step 2: Initialize the client @@ -72,7 +69,7 @@ Copy and paste the following code into your `src/main.rs` file. ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::Duration; use miden_client::{ @@ -105,10 +102,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) @@ -242,7 +239,7 @@ Your updated `main()` function in `src/main.rs` should look like this: ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::Duration; use miden_client::{ @@ -275,10 +272,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/docs/src/rust-client/creating_notes_in_masm_tutorial.md b/docs/src/rust-client/creating_notes_in_masm_tutorial.md index f513160e..fd4fd845 100644 --- a/docs/src/rust-client/creating_notes_in_masm_tutorial.md +++ b/docs/src/rust-client/creating_notes_in_masm_tutorial.md @@ -55,10 +55,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ## Step 2: Write the Note Script @@ -80,7 +77,7 @@ masm/ └── notes/ ``` -Inside the `masm/notes/` directory, create the file `iterative_output_note.masm`: +Inside the `masm/notes/` directory, create the file `iterative_output_note.masm`. Note scripts in v0.14.5+ are compiled as libraries; the `@note_script` attribute marks the entrypoint procedure. ```masm use miden::protocol::active_note @@ -97,8 +94,10 @@ const ASSET_HALF_VALUE_PTR=8 # half-amount ASSET_VALUE stored here const ACCOUNT_ID_PREFIX=12 # storage: [prefix, suffix, tag, 0] const TAG=14 # = ACCOUNT_ID_PREFIX + 2 -# => [] -begin +#! Inputs: [] +#! Outputs: [] +@note_script +pub proc main # Drop word if user accidentally pushes note_args dropw # => [] diff --git a/docs/src/rust-client/custom_note_how_to.md b/docs/src/rust-client/custom_note_how_to.md index 4525917e..7459c7b8 100644 --- a/docs/src/rust-client/custom_note_how_to.md +++ b/docs/src/rust-client/custom_note_how_to.md @@ -49,7 +49,7 @@ Now, combine the minted asset and the secret hash to build the custom note. The 2. **Miden Assembly Code:** - The Miden assembly note script ensures that the note can only be consumed if the provided secret, when hashed, matches the hash stored in the note input. -Below is the Miden Assembly code for the note: +Below is the Miden Assembly code for the note. Note scripts in v0.14.5+ are compiled as libraries; the `@note_script` attribute marks the entrypoint procedure. ```masm use miden::protocol::active_note @@ -70,7 +70,8 @@ const ERROR_DIGEST_MISMATCH="Expected digest does not match computed digest" #! #! Note storage is assumed to be as follows: #! => EXPECTED_DIGEST -begin +@note_script +pub proc main # => HASH_PREIMAGE_SECRET # Hashing the secret number hash diff --git a/docs/src/rust-client/delegated_proving_tutorial.md b/docs/src/rust-client/delegated_proving_tutorial.md index e3413ce8..7d443822 100644 --- a/docs/src/rust-client/delegated_proving_tutorial.md +++ b/docs/src/rust-client/delegated_proving_tutorial.md @@ -52,10 +52,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ## Step 2: Initialize the client and prover and construct transactions @@ -67,7 +64,7 @@ We construct a `LocalTransactionProver` for this walkthrough. use miden_client::auth::AuthSecretKey; use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use miden_client::{ account::component::BasicWallet, @@ -93,10 +90,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/docs/src/rust-client/mint_consume_create_tutorial.md b/docs/src/rust-client/mint_consume_create_tutorial.md index 302aca40..6d050f44 100644 --- a/docs/src/rust-client/mint_consume_create_tutorial.md +++ b/docs/src/rust-client/mint_consume_create_tutorial.md @@ -251,7 +251,7 @@ Your `src/main.rs` function should now look like this: ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::Duration; use miden_client::{ @@ -284,10 +284,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/docs/src/rust-client/network_transactions_tutorial.md b/docs/src/rust-client/network_transactions_tutorial.md index dccf3996..83993540 100644 --- a/docs/src/rust-client/network_transactions_tutorial.md +++ b/docs/src/rust-client/network_transactions_tutorial.md @@ -53,10 +53,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ## Step 2: Set up MASM files @@ -124,12 +121,15 @@ This script executes a function call (increment) that creates a necessary state ### Network Note for User Interaction -Create `masm/notes/network_increment_note.masm`: +Create `masm/notes/network_increment_note.masm`. Note scripts in v0.14.5+ are compiled as libraries; the `@note_script` attribute marks the entrypoint procedure. ```masm use external_contract::counter_contract -begin +#! Inputs: [] +#! Outputs: [] +@note_script +pub proc main call.counter_contract::increment_count end ``` diff --git a/docs/src/rust-client/oracle_tutorial.md b/docs/src/rust-client/oracle_tutorial.md index 3ffe6b4d..5d970f3f 100644 --- a/docs/src/rust-client/oracle_tutorial.md +++ b/docs/src/rust-client/oracle_tutorial.md @@ -53,7 +53,6 @@ use miden_client::{ account::{ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, AccountStorageMode, AccountType, StorageMapKey, StorageSlot, StorageSlotName, - StorageSlotType, }, auth::NoAuth, builder::ClientBuilder, @@ -63,89 +62,69 @@ use miden_client::{ Endpoint, GrpcClient, }, transaction::{ForeignAccount, TransactionRequestBuilder}, - Client, ClientError, Word, + Client, ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; use rand::RngCore; use std::{path::PathBuf, sync::Arc}; -/// Import the oracle + its publishers and return the ForeignAccount list -/// Due to Pragma's decentralized oracle architecture, we need to get the -/// list of all data publisher accounts to read price from via a nested FPI call +// BTC/USD pair encoding per `astraly-labs/pragma-miden`. +const PAIR_PREFIX: u64 = 1; +const PAIR_SUFFIX: u64 = 0; + +// Pragma oracle storage slot names. +const SLOT_NEXT_INDEX: &str = "pragma::oracle::next_publisher_index"; +const SLOT_PUBLISHERS: &str = "pragma::oracle::publishers"; +const SLOT_ENTRIES: &str = "pragma::publisher::entries"; + +// Procedure root of `get_median` on the deployed Pragma oracle. +// DEPLOYMENT-SPECIFIC: re-verify after each Pragma redeploy. Source of truth: +// https://github.com/astraly-labs/pragma-miden#deployments +const GET_MEDIAN_PROC_HASH: &str = + "0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9"; + +/// Walks the Pragma oracle's storage to import the oracle and all of its +/// publisher accounts as `ForeignAccount`s, with each publisher's +/// `pragma::publisher::entries` map gated on the supplied `pair_word`. +/// +/// `pair_word` is `[prefix, suffix, 0, 0]`. For BTC/USD per Pragma's +/// convention, build it from `PAIR_PREFIX = 1, PAIR_SUFFIX = 0`. +/// +/// Mirrors `astraly-labs/pragma-miden/examples/consume-price/src/main.rs`. pub async fn get_oracle_foreign_accounts( client: &mut Client, oracle_account_id: AccountId, - trading_pair: u64, + pair_word: Word, ) -> Result, ClientError> { client.import_account_by_id(oracle_account_id).await?; + println!("Imported oracle account: {}", oracle_account_id); - let oracle_account = client + let oracle = client .get_account(oracle_account_id) - .await - .expect("RPC failed") + .await? .expect("oracle account not found"); + let storage = oracle.storage(); - let storage = oracle_account.storage(); - let publisher_count_slot = storage - .slots() - .iter() - .find(|slot| { - let name = slot.name().as_str(); - name.contains("publisher") && name.contains("count") - }) - .map(|slot| slot.name().clone()) - .or_else(|| storage.slots().first().map(|slot| slot.name().clone())) - .expect("oracle storage is expected to have at least one slot"); - - let publisher_count = storage - .get_item(&publisher_count_slot) - .map(|word| word[0].as_canonical_u64()) - .unwrap_or(0); - - let publisher_id_slots: Vec = storage - .slots() - .iter() - .filter(|slot| slot.slot_type() == StorageSlotType::Value) - .filter(|slot| slot.name() != &publisher_count_slot) - .map(|slot| slot.name().clone()) - .collect(); - - let publisher_ids: Vec = publisher_id_slots - .iter() - .take(publisher_count.saturating_sub(1) as usize) - .filter_map(|slot_name| storage.get_item(slot_name).ok()) - .map(|digest| { - let words: Word = digest.into(); - AccountId::new_unchecked([words[3], words[2]]) - }) - .collect(); - - let mut foreign_accounts = Vec::with_capacity(publisher_ids.len() + 1); - let empty_keys: [StorageMapKey; 0] = []; - - for pid in publisher_ids { - client.import_account_by_id(pid).await?; + let count_slot = StorageSlotName::new(SLOT_NEXT_INDEX).expect("valid slot name"); + let publishers_slot = StorageSlotName::new(SLOT_PUBLISHERS).expect("valid slot name"); + let entries_slot = StorageSlotName::new(SLOT_ENTRIES).expect("valid slot name"); - let publisher_account = client - .get_account(pid) - .await - .expect("RPC failed") - .expect("publisher account not found"); - let map_slot_names: Vec = publisher_account - .storage() - .slots() - .iter() - .filter(|slot| slot.slot_type() == StorageSlotType::Map) - .map(|slot| slot.name().clone()) - .collect(); - - let storage_requirements = AccountStorageRequirements::new( - map_slot_names - .iter() - .map(|slot_name| (slot_name.clone(), empty_keys.iter())), - ); + let publisher_count = storage.get_item(&count_slot)?[0].as_canonical_u64(); + let pair_key = StorageMapKey::new(pair_word); - foreign_accounts.push(ForeignAccount::public(pid, storage_requirements)?); + // Pragma reserves indices 0 and 1 as sentinels; real publishers start at 2. + let mut foreign_accounts = Vec::with_capacity(publisher_count.saturating_sub(2) as usize + 1); + for i in 2..publisher_count { + let key: Word = [Felt::new(i), Felt::ZERO, Felt::ZERO, Felt::ZERO].into(); + let w: Word = storage.get_map_item(&publishers_slot, key)?; + // The publisher record stores `[suffix, prefix, ...]` at indices [2..3]. + let pid = AccountId::new_unchecked([w[3], w[2]]); + + client.import_account_by_id(pid).await?; + foreign_accounts.push(ForeignAccount::public( + pid, + AccountStorageRequirements::new([(entries_slot.clone(), [pair_key].iter())]), + )?); } foreign_accounts.push(ForeignAccount::public( @@ -156,6 +135,19 @@ pub async fn get_oracle_foreign_accounts( Ok(foreign_accounts) } +/// Fills the `oracle_reader.masm` template with deployment-specific values. +fn oracle_reader_source(oracle_id: AccountId) -> String { + include_str!("../masm/accounts/oracle_reader.masm") + .replace("{pair_prefix}", &PAIR_PREFIX.to_string()) + .replace("{pair_suffix}", &PAIR_SUFFIX.to_string()) + .replace("{get_median_proc_hash}", GET_MEDIAN_PROC_HASH) + .replace("{oracle_id_prefix}", &u64::from(oracle_id.prefix()).to_string()) + .replace( + "{oracle_id_suffix}", + &oracle_id.suffix().as_canonical_u64().to_string(), + ) +} + #[tokio::main] async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- @@ -181,39 +173,64 @@ async fn main() -> Result<(), ClientError> { println!("Latest block: {}", client.sync_state().await?.block_num); // ------------------------------------------------------------------------- - // Get all foreign accounts for oracle data + // Parse and validate oracle account ID from CLI // ------------------------------------------------------------------------- - // The Pragma oracle bech32 ID is read from the first CLI argument. - // Live IDs rotate per testnet iteration; the canonical source for the - // current testnet oracle is the `astraly-labs/pragma-miden` README: + // `AccountId::parse` accepts both bech32 (`mtst1...`) and hex (`0x...`) + // forms. Live Pragma oracle IDs rotate per testnet iteration; the + // canonical source is the `astraly-labs/pragma-miden` README: // https://github.com/astraly-labs/pragma-miden#deployments - // Run as: `cargo run --release --bin oracle_data_query -- ` - let oracle_bech32 = std::env::args() - .nth(1) - .expect("Usage: oracle_data_query "); - let (_, oracle_account_id) = AccountId::from_bech32(&oracle_bech32).unwrap(); - let btc_usd_pair_id = 120195681; - let foreign_accounts: Vec = - get_oracle_foreign_accounts(&mut client, oracle_account_id, btc_usd_pair_id).await?; - + // Run as: + // cargo run --release --bin oracle_data_query -- + let oracle_arg = std::env::args().nth(1).expect( + "Usage: oracle_data_query -- pass the current testnet oracle ID from https://github.com/astraly-labs/pragma-miden#deployments", + ); + let (oracle_account_id, _network) = + AccountId::parse(&oracle_arg).expect("Invalid account ID format (expected bech32 or hex)"); println!( - "Oracle accountId prefix: {:?} suffix: {:?}", - oracle_account_id.prefix(), - oracle_account_id.suffix() + "Parsed oracle ID: prefix={}, suffix={}", + u64::from(oracle_account_id.prefix()), + oracle_account_id.suffix().as_canonical_u64(), ); + // ------------------------------------------------------------------------- + // Get all foreign accounts for oracle data (BTC/USD) + // ------------------------------------------------------------------------- + let pair_word: Word = [ + Felt::new(PAIR_PREFIX), + Felt::new(PAIR_SUFFIX), + Felt::ZERO, + Felt::ZERO, + ] + .into(); + let foreign_accounts: Vec = + get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; + // ------------------------------------------------------------------------- // Create Oracle Reader contract // ------------------------------------------------------------------------- - // `include_str!` resolves at compile time relative to this source file, - // so the binary is independent of the working directory it is run from. - let contract_code = include_str!("../masm/accounts/oracle_reader.masm"); + // `oracle_reader.masm` is a *template*: the placeholders `{pair_prefix}`, + // `{pair_suffix}`, `{get_median_proc_hash}`, `{oracle_id_prefix}`, and + // `{oracle_id_suffix}` are substituted by the Rust binary before the + // MASM is compiled. The raw file is **not** standalone valid MASM. + let contract_code: String = oracle_reader_source(oracle_account_id); + + // Defensive gate: fail loudly if any placeholder leaked through, rather + // than letting the assembler emit an opaque parse error. + if let Some(bad) = contract_code + .lines() + .find(|l| l.contains('{') || l.contains('}')) + { + panic!( + "oracle_reader template substitution incomplete; offending line: `{}`", + bad.trim() + ); + } let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); let contract_component_code = client .code_builder() - .compile_component_code("external_contract::oracle_reader", contract_code) + .compile_component_code("external_contract::oracle_reader", contract_code.as_str()) .unwrap(); let contract_component = AccountComponent::new( contract_component_code, @@ -225,7 +242,7 @@ async fn main() -> Result<(), ClientError> { let mut seed = [0_u8; 32]; client.rng().fill_bytes(&mut seed); - let oracle_reader_contract = miden_client::account::AccountBuilder::new(seed) + let oracle_reader_contract = AccountBuilder::new(seed) .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(contract_component.clone()) @@ -249,7 +266,7 @@ async fn main() -> Result<(), ClientError> { // miden-vm#2778). let tx_script = client .code_builder() - .with_linked_module("external_contract::oracle_reader", contract_code) + .with_linked_module("external_contract::oracle_reader", contract_code.as_str()) .unwrap() .compile_tx_script(script_code) .unwrap(); @@ -278,10 +295,10 @@ async fn main() -> Result<(), ClientError> { _Don't run this code just yet, we still need to create our smart contract that queries the oracle_ -The oracle bech32 ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is `120195681`. The `get_oracle_foreign_accounts` function returns all of the `ForeignAccounts` that you will need to execute the transaction to get the price data from the oracle. Since Pragma's oracle depends on multiple publishers, this function queries all of the publisher account ids required to make a successful FPI call. +The oracle account ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is encoded as `[PAIR_PREFIX = 1, PAIR_SUFFIX = 0, 0, 0]` per [Pragma's pair convention](https://github.com/astraly-labs/pragma-miden/blob/main/examples/consume-price/src/main.rs). The `get_oracle_foreign_accounts` function mirrors the walk in Pragma's `consume-price` example: read `pragma::oracle::next_publisher_index`, walk the `pragma::oracle::publishers` map for `i in 2..publisher_count` (Pragma reserves 0 and 1 as sentinels), then for each publisher request its `pragma::publisher::entries` map gated on the `pair_word`. The `trading_pair` requirement is therefore explicit in the function signature; passing a different `pair_word` reads a different price. :::note -The oracle account ID, procedure hash, and trading pair ID used in this tutorial reference Pragma's testnet deployment. Live IDs rotate per testnet iteration; the canonical source for the current testnet oracle is the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments). +The live oracle path is currently blocked pending a v0.14-compatible Pragma deployment. Pragma's source repo is on `miden-protocol` v0.13; the migration is tracked in [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40) (open PR). After Pragma migrates, the oracle account ID and the `get_median` procedure hash hardcoded in `oracle_data_query.rs` (constant `GET_MEDIAN_PROC_HASH`) must be re-verified against the new deployment. ::: ## Step 2: Build the price reader smart contract and script @@ -306,34 +323,40 @@ masm/ ### Oracle price reader smart contract -Below is our oracle price reader contract. It has a a single exported procedure: `get_price` +Below is our oracle price reader contract. It has a single exported procedure: `get_price`. -The import `miden::tx` contains the `tx::execute_foreign_procedure` which we will use to read the price from the oracle contract. +The import `miden::protocol::tx` contains `tx::execute_foreign_procedure`, which we use to read the price from the oracle contract. + +This file is a **template**: the placeholders `{pair_prefix}`, `{pair_suffix}`, `{get_median_proc_hash}`, `{oracle_id_prefix}`, and `{oracle_id_suffix}` are substituted by the Rust binary (`oracle_reader_source(...)`) before the MASM is compiled. The raw file as shown here is **not** standalone valid MASM. The pair encoding is deployment-agnostic for any `(PAIR_PREFIX, PAIR_SUFFIX)` trading pair, but `{get_median_proc_hash}` and the oracle account ID are deployment-specific to Pragma's testnet oracle and must be re-verified after every Pragma redeploy. #### Here's a breakdown of what the `get_price` procedure does: -1. Pushes `0.0.0.120195681` onto the stack, representing the BTC/USD pair in the Pragma oracle. -2. Pushes `0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9` onto the stack which is the procedure root of the `get_median` procedure in the oracle. -3. Pushes `939716883672832.2172042075194638080` onto the stack which is the oracle id prefix and suffix. -4. Calls `tx::execute_foreign_procedure` which calls the `get_median` procedure via foreign procedure invocation. +1. Pushes the BTC/USD pair onto the stack as `[0, 0, {pair_suffix}, {pair_prefix}]`. With the pair encoding `(PAIR_PREFIX = 1, PAIR_SUFFIX = 0)`, this becomes `push.0.0.0.1`. +2. Pushes the deployment-specific procedure root of `get_median` onto the stack. +3. Pushes the oracle account ID prefix and suffix onto the stack. +4. Calls `tx::execute_foreign_procedure` to invoke `get_median` via FPI. Inside of the `masm/accounts/` directory, create the `oracle_reader.masm` file: ```masm +# This file is a template, not standalone valid MASM. +# See `oracle_reader_source(...)` in src/main.rs for substitution. + use miden::protocol::tx -# Fetches the current price from the `get_median` -# procedure from the Pragma oracle -# => [] +#! Reads the current price for a single trading pair from the Pragma oracle +#! via foreign procedure invocation. +#! +#! Inputs: [] +#! Outputs: [price] pub proc get_price - push.0.0.0.120195681 + push.0.0.{pair_suffix}.{pair_prefix} # => [PAIR] - # This is the procedure root of the `get_median` procedure - push.0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9 + push.{get_median_proc_hash} # => [GET_MEDIAN_HASH, PAIR] - push.939716883672832.2172042075194638080 + push.{oracle_id_prefix}.{oracle_id_suffix} # => [oracle_id_prefix, oracle_id_suffix, GET_MEDIAN_HASH, PAIR] exec.tx::execute_foreign_procedure @@ -364,10 +387,10 @@ end ## Step 3: Run the program -This tutorial requires a live Pragma oracle deployment. Get the current testnet oracle bech32 ID from the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments) and pass it as a CLI argument: +This tutorial requires a v0.14-compatible Pragma oracle deployment. The current Pragma deployment is on `miden-protocol` v0.13 (see [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40), open), so the live FPI walk will fail until that migration lands. The tutorial code itself is correct and will work the moment Pragma redeploys. Get the current testnet oracle account ID (bech32 or hex) from the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments) and pass it as a CLI argument: ``` -cargo run --release -- +cargo run --release -- ``` The output of our program will look something like this: @@ -409,10 +432,10 @@ To run this tutorial end-to-end, navigate to the `rust-client` directory in the ```bash cd rust-client -cargo run --release --bin oracle_data_query -- +cargo run --release --bin oracle_data_query -- ``` -where `` is Pragma's deployed oracle account ID on testnet. +where `` is Pragma's deployed oracle account ID on testnet. ### Continue learning diff --git a/docs/src/rust-client/public_account_interaction_tutorial.md b/docs/src/rust-client/public_account_interaction_tutorial.md index f80bfb4e..0ffbd136 100644 --- a/docs/src/rust-client/public_account_interaction_tutorial.md +++ b/docs/src/rust-client/public_account_interaction_tutorial.md @@ -41,10 +41,7 @@ miden-client = { version = "0.14", features = ["testing", "tonic"] } miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" } miden-protocol = { version = "0.14" } rand = { version = "0.9" } -serde = { version = "1", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] } -rand_chacha = "0.9.0" ``` ## Step 2: Build the counter contract diff --git a/docs/src/rust-client/unauthenticated_note_how_to.md b/docs/src/rust-client/unauthenticated_note_how_to.md index d9e3560c..436917c2 100644 --- a/docs/src/rust-client/unauthenticated_note_how_to.md +++ b/docs/src/rust-client/unauthenticated_note_how_to.md @@ -55,7 +55,7 @@ Alice ➡ Bob ➡ Charlie ➡ Dave ➡ Eve ➡ Frank ➡ ... ```rust no_run use miden_client::auth::{AuthSchemeId, AuthSingleSig}; use rand::RngCore; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use tokio::time::{sleep, Duration, Instant}; use miden_client::{ @@ -115,10 +115,10 @@ async fn main() -> Result<(), ClientError> { let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms)); // Initialize keystore - let keystore_path = std::path::PathBuf::from("./keystore"); + let keystore_path = PathBuf::from("./keystore"); let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap()); - let store_path = std::path::PathBuf::from("./store.sqlite3"); + let store_path = PathBuf::from("./store.sqlite3"); let mut client = ClientBuilder::new() .rpc(rpc_client) diff --git a/masm/accounts/oracle_reader.masm b/masm/accounts/oracle_reader.masm index 21067a93..4fd50e4a 100644 --- a/masm/accounts/oracle_reader.masm +++ b/masm/accounts/oracle_reader.masm @@ -1,21 +1,30 @@ -# The oracle account ID, procedure hash, and pair ID below reference -# Pragma's testnet deployment (https://github.com/astraly-labs/pragma-miden). -# If Pragma redeploys their oracle, these values must be updated. +# This file is a template, not standalone valid MASM. +# +# The placeholders {pair_prefix}, {pair_suffix}, {get_median_proc_hash}, +# {oracle_id_prefix}, and {oracle_id_suffix} are substituted at compile time +# by the Rust binary (`oracle_reader_source(...)` in `oracle_data_query.rs`) +# before this code is handed to the assembler. The pair encoding is +# deployment-agnostic for any (PAIR_PREFIX, PAIR_SUFFIX) trading pair, but +# the `get_median` procedure root and the oracle account id below are +# Pragma-deployment-specific and must be re-verified after every Pragma +# redeploy. Source of truth: +# https://github.com/astraly-labs/pragma-miden#deployments use miden::protocol::tx -# Fetches the current price from the `get_median` -# procedure from the Pragma oracle -# => [] +#! Reads the current price for a single trading pair from the Pragma oracle +#! via foreign procedure invocation. +#! +#! Inputs: [] +#! Outputs: [price] pub proc get_price - push.0.0.0.120195681 + push.0.0.{pair_suffix}.{pair_prefix} # => [PAIR] - # This is the procedure root of the `get_median` procedure - push.0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9 + push.{get_median_proc_hash} # => [GET_MEDIAN_HASH, PAIR] - push.939716883672832.2172042075194638080 + push.{oracle_id_prefix}.{oracle_id_suffix} # => [oracle_id_prefix, oracle_id_suffix, GET_MEDIAN_HASH, PAIR] exec.tx::execute_foreign_procedure diff --git a/masm/notes/hash_preimage_note.masm b/masm/notes/hash_preimage_note.masm index 4c54840a..fc18ed48 100644 --- a/masm/notes/hash_preimage_note.masm +++ b/masm/notes/hash_preimage_note.masm @@ -16,7 +16,8 @@ const ERROR_DIGEST_MISMATCH="Expected digest does not match computed digest" #! #! Note storage is assumed to be as follows: #! => EXPECTED_DIGEST -begin +@note_script +pub proc main # => HASH_PREIMAGE_SECRET # Hashing the secret number hash diff --git a/masm/notes/iterative_output_note.masm b/masm/notes/iterative_output_note.masm index dd193014..6a812497 100644 --- a/masm/notes/iterative_output_note.masm +++ b/masm/notes/iterative_output_note.masm @@ -12,8 +12,10 @@ const ASSET_HALF_VALUE_PTR=8 # half-amount ASSET_VALUE stored here const ACCOUNT_ID_PREFIX=12 # storage: [prefix, suffix, tag, 0] const TAG=14 # = ACCOUNT_ID_PREFIX + 2 -# => [] -begin +#! Inputs: [] +#! Outputs: [] +@note_script +pub proc main # Drop word if user accidentally pushes note_args dropw # => [] diff --git a/masm/notes/network_increment_note.masm b/masm/notes/network_increment_note.masm index a0e1a0c7..3b26cecd 100644 --- a/masm/notes/network_increment_note.masm +++ b/masm/notes/network_increment_note.masm @@ -1,5 +1,8 @@ use external_contract::counter_contract -begin +#! Inputs: [] +#! Outputs: [] +@note_script +pub proc main call.counter_contract::increment_count end diff --git a/rust-client/Cargo.lock b/rust-client/Cargo.lock index c9e7d9a4..b2afebbe 100644 --- a/rust-client/Cargo.lock +++ b/rust-client/Cargo.lock @@ -420,6 +420,45 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "colorchoice" version = "1.0.5" @@ -1236,7 +1275,7 @@ dependencies = [ "hyper", "libc", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1571,7 +1610,7 @@ dependencies = [ "miden-utils-sync", "primitive-types", "regex", - "thiserror", + "thiserror 2.0.18", "walkdir", ] @@ -1584,7 +1623,7 @@ dependencies = [ "miden-core", "miden-crypto", "miden-utils-indexing", - "thiserror", + "thiserror 2.0.18", "tracing", ] @@ -1602,7 +1641,7 @@ dependencies = [ "miden-package-registry", "miden-project", "smallvec", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1627,7 +1666,7 @@ dependencies = [ "semver 1.0.28", "serde", "smallvec", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1637,14 +1676,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde56bcea3cebe307786a856e204d84e7987c318e5a2909bcbb655d16286ce31" dependencies = [ "miden-protocol", - "thiserror", + "thiserror 2.0.18", ] [[package]] name = "miden-client" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf411027beb1db257a403f3a53f5e6ea6cb8998ec8dcafaa4995cee51ab7c68" +checksum = "15007f2cf4e80316a8141665b43f454e77ce0dfd2ac0307ce6cdf7a5d552d58b" dependencies = [ "anyhow", "async-trait", @@ -1653,6 +1692,7 @@ dependencies = [ "getrandom 0.3.4", "gloo-timers", "hex", + "miden-debug", "miden-node-proto-build", "miden-note-transport-proto-build", "miden-protocol", @@ -1667,7 +1707,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror", + "thiserror 2.0.18", "tokio", "tonic", "tonic-health", @@ -1681,9 +1721,9 @@ dependencies = [ [[package]] name = "miden-client-sqlite-store" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16f63ecd582f0371218c88a188d7b73ba3d92fb39d9452b4fd31a44dbaec0d2" +checksum = "53a6d9c9bf443b9df440c010eeab6916ab6bc529faed5eb19f84ab7bc21aad59" dependencies = [ "anyhow", "async-trait", @@ -1694,7 +1734,7 @@ dependencies = [ "miden-protocol", "rusqlite", "rusqlite_migration", - "thiserror", + "thiserror 2.0.18", "tokio", ] @@ -1717,7 +1757,7 @@ dependencies = [ "proptest", "proptest-derive", "serde", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1734,7 +1774,7 @@ dependencies = [ "miden-package-registry", "miden-processor", "miden-utils-sync", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1776,7 +1816,7 @@ dependencies = [ "sha2", "sha3", "subtle", - "thiserror", + "thiserror 2.0.18", "x25519-dalek", ] @@ -1790,6 +1830,77 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "miden-debug" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df04a684eeb96efabc63e2800a01945f7f6e19ffd00d3b49064e85f7e1fac444" +dependencies = [ + "clap", + "futures", + "glob", + "log", + "miden-assembly", + "miden-assembly-syntax", + "miden-core", + "miden-crypto", + "miden-debug-dap", + "miden-debug-engine", + "miden-debug-types", + "miden-mast-package", + "miden-processor", + "miden-protocol", + "miden-thiserror", + "miden-tx", + "num-traits", + "rustc-demangle", + "serde", + "serde_json", + "smallvec", + "socket2 0.5.10", + "tokio", + "tokio-util", + "toml 0.8.23", +] + +[[package]] +name = "miden-debug-dap" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cd41176322df12836bb4deecd4b619f7cf8239ed7b2c4ac1da7b2830e5199c" +dependencies = [ + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "miden-debug-engine" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03dd00bd4dab99dbfdec9fd07009811a7e3b3a988e74a28c6ccb735ac34e138" +dependencies = [ + "clap", + "glob", + "log", + "miden-assembly", + "miden-assembly-syntax", + "miden-core", + "miden-debug-dap", + "miden-debug-types", + "miden-mast-package", + "miden-processor", + "miden-thiserror", + "miden-tx", + "num-traits", + "rustc-demangle", + "serde", + "serde_json", + "smallvec", + "socket2 0.5.10", + "toml 0.8.23", +] + [[package]] name = "miden-debug-types" version = "0.22.1" @@ -1804,8 +1915,8 @@ dependencies = [ "miden-utils-sync", "paste", "serde", - "serde_spanned", - "thiserror", + "serde_spanned 1.1.1", + "thiserror 2.0.18", ] [[package]] @@ -1823,7 +1934,7 @@ dependencies = [ "rand 0.10.1", "serde", "subtle", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1846,7 +1957,7 @@ dependencies = [ "miden-core", "miden-debug-types", "serde", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1869,7 +1980,7 @@ dependencies = [ "strip-ansi-escapes", "syn 2.0.117", "textwrap", - "thiserror", + "thiserror 2.0.18", "trybuild", "unicode-width 0.1.14", ] @@ -1887,9 +1998,9 @@ dependencies = [ [[package]] name = "miden-node-proto-build" -version = "0.14.8" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc6d12ffab3127e7d75dada761fbd78ef9f9cdb107e33110a4c86e014781e02" +checksum = "6e457758c9a6aa5f70f687f1081833f102572ee1d02baf549625868917495815" dependencies = [ "build-rs", "fs-err", @@ -1922,7 +2033,7 @@ dependencies = [ "pubgrub", "serde", "smallvec", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -1939,7 +2050,7 @@ dependencies = [ "miden-utils-indexing", "paste", "rayon", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -1956,15 +2067,15 @@ dependencies = [ "miden-package-registry", "serde", "serde-untagged", - "thiserror", - "toml", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", ] [[package]] name = "miden-protocol" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e860cc978d3467297de076e9bd22f0573b82ef73a3d223d6bb957731a45b8164" +checksum = "140b1b3d6b2a3a2bfaeca8b0d2ca15a8ee6a7e0abebbc4f1a2a3a1f1b7f7f58d" dependencies = [ "bech32", "fs-err", @@ -1985,8 +2096,8 @@ dependencies = [ "regex", "semver 1.0.28", "serde", - "thiserror", - "toml", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", "walkdir", ] @@ -2014,16 +2125,16 @@ dependencies = [ "miden-debug-types", "miden-processor", "serde", - "thiserror", + "thiserror 2.0.18", "tokio", "tracing", ] [[package]] name = "miden-remote-prover-client" -version = "0.14.8" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba0f6545b6b7fdfdd7d57da4b2cbe546bdfc5e177f14c3086e2761ad65afae7d" +checksum = "5a388883f27bff95127c962669eb5d294d27379ac357d6dac5c77faa81b30def" dependencies = [ "build-rs", "fs-err", @@ -2033,7 +2144,7 @@ dependencies = [ "miden-tx", "miette", "prost", - "thiserror", + "thiserror 2.0.18", "tokio", "tonic", "tonic-prost", @@ -2053,9 +2164,9 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f455a087f41c30636b45ead961d1e66114d2d20661887b307cede05307eeb942" +checksum = "a08e4e4edb289460b1b676a9cdb1727131c2749218dd85a333571d09e1cfa621" dependencies = [ "fs-err", "miden-assembly", @@ -2065,7 +2176,7 @@ dependencies = [ "miden-protocol", "rand 0.9.4", "regex", - "thiserror", + "thiserror 2.0.18", "walkdir", ] @@ -2089,7 +2200,27 @@ dependencies = [ "miden-tx-batch-prover", "rand 0.9.4", "rand_chacha", - "thiserror", + "thiserror 2.0.18", +] + +[[package]] +name = "miden-thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183ff8de338956ecfde3a38573241eb7a6f3d44d73866c210e5629c07fa00253" +dependencies = [ + "miden-thiserror-impl", +] + +[[package]] +name = "miden-thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee4176a0f2e7d29d2a8ee7e60b6deb14ce67a20e94c3e2c7275cdb8804e1862" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2103,7 +2234,7 @@ dependencies = [ "miden-prover", "miden-standards", "miden-verifier", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2148,7 +2279,7 @@ checksum = "c8834e76299686bcce3de1685158aa4cff49b7fa5e0e00a6cc811e8f2cf5775f" dependencies = [ "miden-crypto", "serde", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2174,7 +2305,7 @@ dependencies = [ "miden-core", "miden-crypto", "serde", - "thiserror", + "thiserror 2.0.18", "tracing", ] @@ -2189,7 +2320,7 @@ dependencies = [ "serde", "serde_repr", "smallvec", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2564,7 +2695,7 @@ dependencies = [ "p3-field", "p3-matrix", "p3-util", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2583,7 +2714,7 @@ dependencies = [ "p3-miden-transcript", "p3-util", "rand 0.10.1", - "thiserror", + "thiserror 2.0.18", "tracing", ] @@ -2604,7 +2735,7 @@ dependencies = [ "p3-miden-stateful-hasher", "p3-miden-transcript", "p3-util", - "thiserror", + "thiserror 2.0.18", "tracing", ] @@ -2624,7 +2755,7 @@ dependencies = [ "p3-util", "rand 0.10.1", "serde", - "thiserror", + "thiserror 2.0.18", "tracing", ] @@ -2647,7 +2778,7 @@ dependencies = [ "p3-challenger", "p3-field", "serde", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -3034,7 +3165,7 @@ dependencies = [ "prost-reflect", "prost-types", "protox-parse", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -3046,7 +3177,7 @@ dependencies = [ "logos", "miette", "prost-types", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -3059,7 +3190,7 @@ dependencies = [ "log", "priority-queue", "rustc-hash", - "thiserror", + "thiserror 2.0.18", "version-ranges", ] @@ -3585,6 +3716,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -3667,6 +3807,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -3738,6 +3888,12 @@ dependencies = [ "vte", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -3863,13 +4019,33 @@ dependencies = [ "unicode-width 0.2.2", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -3911,7 +4087,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] @@ -3962,6 +4138,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", +] + [[package]] name = "toml" version = "1.1.2+spec-1.1.0" @@ -3970,11 +4159,20 @@ checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned", - "toml_datetime", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.1", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", ] [[package]] @@ -3986,15 +4184,35 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.1", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -4020,7 +4238,7 @@ dependencies = [ "percent-encoding", "pin-project", "rustls-native-certs", - "socket2", + "socket2 0.6.3", "sync_wrapper", "tokio", "tokio-rustls", @@ -4099,7 +4317,7 @@ dependencies = [ "httparse", "js-sys", "pin-project", - "thiserror", + "thiserror 2.0.18", "tonic", "tower-service", "wasm-bindgen", @@ -4229,7 +4447,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml", + "toml 1.1.2+spec-1.1.0", ] [[package]] @@ -4679,6 +4897,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "1.0.1" diff --git a/rust-client/src/bin/oracle_data_query.rs b/rust-client/src/bin/oracle_data_query.rs index 5387691f..3b98a98e 100644 --- a/rust-client/src/bin/oracle_data_query.rs +++ b/rust-client/src/bin/oracle_data_query.rs @@ -2,7 +2,6 @@ use miden_client::{ account::{ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId, AccountStorageMode, AccountType, StorageMapKey, StorageSlot, StorageSlotName, - StorageSlotType, }, auth::NoAuth, builder::ClientBuilder, @@ -12,89 +11,75 @@ use miden_client::{ Endpoint, GrpcClient, }, transaction::{ForeignAccount, TransactionRequestBuilder}, - Client, ClientError, Word, + Client, ClientError, Felt, Word, }; use miden_client_sqlite_store::ClientBuilderSqliteExt; use rand::RngCore; use std::{path::PathBuf, sync::Arc}; -/// Import the oracle + its publishers and return the ForeignAccount list -/// Due to Pragma's decentralized oracle architecture, we need to get the -/// list of all data publisher accounts to read price from via a nested FPI call +// BTC/USD pair encoding per `astraly-labs/pragma-miden`. +const PAIR_PREFIX: u64 = 1; +const PAIR_SUFFIX: u64 = 0; + +// Pragma oracle storage slot names. Match the constants in +// `astraly-labs/pragma-miden/crates/accounts/src/oracle/oracle.masm` +// and `publisher/publisher.masm`. +const SLOT_NEXT_INDEX: &str = "pragma::oracle::next_publisher_index"; +const SLOT_PUBLISHERS: &str = "pragma::oracle::publishers"; +const SLOT_ENTRIES: &str = "pragma::publisher::entries"; + +// Procedure root of the `get_median` procedure on the deployed Pragma oracle +// contract. DEPLOYMENT-SPECIFIC: this hash must be re-verified after every +// Pragma redeploy. Source of truth: +// https://github.com/astraly-labs/pragma-miden#deployments +const GET_MEDIAN_PROC_HASH: &str = + "0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9"; + +/// Walks the Pragma oracle's storage to import the oracle and all of its +/// publisher accounts as `ForeignAccount`s. Each publisher's +/// `pragma::publisher::entries` map is gated on the supplied `pair_word`, +/// so the returned foreign-account list only proves what's needed to read +/// that single trading pair via FPI. +/// +/// `pair_word` is a 4-element Word `[prefix, suffix, 0, 0]`. For BTC/USD +/// per Pragma's convention, build it from `PAIR_PREFIX = 1, PAIR_SUFFIX = 0`. +/// +/// Mirrors the walk pattern in +/// `astraly-labs/pragma-miden/examples/consume-price/src/main.rs`. pub async fn get_oracle_foreign_accounts( client: &mut Client, oracle_account_id: AccountId, - trading_pair: u64, + pair_word: Word, ) -> Result, ClientError> { client.import_account_by_id(oracle_account_id).await?; + println!("Imported oracle account: {}", oracle_account_id); - let oracle_record = client + let oracle = client .get_account(oracle_account_id) - .await - .expect("RPC failed") + .await? .expect("oracle account not found"); + let storage = oracle.storage(); - let storage = oracle_record.storage(); - let publisher_count_slot = storage - .slots() - .iter() - .find(|slot| { - let name = slot.name().as_str(); - name.contains("publisher") && name.contains("count") - }) - .map(|slot| slot.name().clone()) - .or_else(|| storage.slots().first().map(|slot| slot.name().clone())) - .expect("oracle storage is expected to have at least one slot"); - - let publisher_count = storage - .get_item(&publisher_count_slot) - .map(|word| word[0].as_canonical_u64()) - .unwrap_or(0); - - let publisher_id_slots: Vec = storage - .slots() - .iter() - .filter(|slot| slot.slot_type() == StorageSlotType::Value) - .filter(|slot| slot.name() != &publisher_count_slot) - .map(|slot| slot.name().clone()) - .collect(); - - let publisher_ids: Vec = publisher_id_slots - .iter() - .take(publisher_count.saturating_sub(1) as usize) - .filter_map(|slot_name| storage.get_item(slot_name).ok()) - .map(|digest| { - let words: Word = digest.into(); - AccountId::new_unchecked([words[3], words[2]]) - }) - .collect(); - - let mut foreign_accounts = Vec::with_capacity(publisher_ids.len() + 1); - let empty_keys: [StorageMapKey; 0] = []; - - for pid in publisher_ids { - client.import_account_by_id(pid).await?; + let count_slot = StorageSlotName::new(SLOT_NEXT_INDEX).expect("valid slot name"); + let publishers_slot = StorageSlotName::new(SLOT_PUBLISHERS).expect("valid slot name"); + let entries_slot = StorageSlotName::new(SLOT_ENTRIES).expect("valid slot name"); - let publisher_record = client - .get_account(pid) - .await - .expect("RPC failed") - .expect("publisher account not found"); - let map_slot_names: Vec = publisher_record - .storage() - .slots() - .iter() - .filter(|slot| slot.slot_type() == StorageSlotType::Map) - .map(|slot| slot.name().clone()) - .collect(); - - let storage_requirements = AccountStorageRequirements::new( - map_slot_names - .iter() - .map(|slot_name| (slot_name.clone(), empty_keys.iter())), - ); + let publisher_count = storage.get_item(&count_slot)?[0].as_canonical_u64(); + let pair_key = StorageMapKey::new(pair_word); + + // Pragma reserves indices 0 and 1 as sentinels; real publishers start at 2. + let mut foreign_accounts = Vec::with_capacity(publisher_count.saturating_sub(2) as usize + 1); + for i in 2..publisher_count { + let key: Word = [Felt::new(i), Felt::ZERO, Felt::ZERO, Felt::ZERO].into(); + let w: Word = storage.get_map_item(&publishers_slot, key)?; + // The publisher record stores `[suffix, prefix, ...]` at indices [2..3]. + let pid = AccountId::new_unchecked([w[3], w[2]]); - foreign_accounts.push(ForeignAccount::public(pid, storage_requirements)?); + client.import_account_by_id(pid).await?; + foreign_accounts.push(ForeignAccount::public( + pid, + AccountStorageRequirements::new([(entries_slot.clone(), [pair_key].iter())]), + )?); } foreign_accounts.push(ForeignAccount::public( @@ -105,6 +90,19 @@ pub async fn get_oracle_foreign_accounts( Ok(foreign_accounts) } +/// Fills the `oracle_reader.masm` template with deployment-specific values. +fn oracle_reader_source(oracle_id: AccountId) -> String { + include_str!("../../../masm/accounts/oracle_reader.masm") + .replace("{pair_prefix}", &PAIR_PREFIX.to_string()) + .replace("{pair_suffix}", &PAIR_SUFFIX.to_string()) + .replace("{get_median_proc_hash}", GET_MEDIAN_PROC_HASH) + .replace("{oracle_id_prefix}", &u64::from(oracle_id.prefix()).to_string()) + .replace( + "{oracle_id_suffix}", + &oracle_id.suffix().as_canonical_u64().to_string(), + ) +} + #[tokio::main] async fn main() -> Result<(), ClientError> { // ------------------------------------------------------------------------- @@ -130,39 +128,65 @@ async fn main() -> Result<(), ClientError> { println!("Latest block: {}", client.sync_state().await?.block_num); // ------------------------------------------------------------------------- - // Get all foreign accounts for oracle data + // Parse and validate oracle account ID from CLI // ------------------------------------------------------------------------- - // The Pragma oracle bech32 ID is read from the first CLI argument. - // Live IDs rotate per testnet iteration; the canonical source for the - // current testnet oracle is the `astraly-labs/pragma-miden` README: + // The Pragma oracle account ID is read from the first CLI argument and + // accepted in either bech32 (`mtst1...`) or hex (`0x...`) form via + // `AccountId::parse`. Live IDs rotate per testnet iteration; the + // canonical source is the `astraly-labs/pragma-miden` README: // https://github.com/astraly-labs/pragma-miden#deployments - // Run as: `cargo run --release --bin oracle_data_query -- ` - let oracle_bech32 = std::env::args() - .nth(1) - .expect("Usage: oracle_data_query "); - let (_, oracle_account_id) = AccountId::from_bech32(&oracle_bech32).unwrap(); - let btc_usd_pair_id = 120195681; - let foreign_accounts: Vec = - get_oracle_foreign_accounts(&mut client, oracle_account_id, btc_usd_pair_id).await?; - + // Run as: + // cargo run --release --bin oracle_data_query -- + let oracle_arg = std::env::args().nth(1).expect( + "Usage: oracle_data_query -- pass the current testnet oracle ID from https://github.com/astraly-labs/pragma-miden#deployments", + ); + let (oracle_account_id, _network) = + AccountId::parse(&oracle_arg).expect("Invalid account ID format (expected bech32 or hex)"); println!( - "Oracle accountId prefix: {:?} suffix: {:?}", - oracle_account_id.prefix(), - oracle_account_id.suffix() + "Parsed oracle ID: prefix={}, suffix={}", + u64::from(oracle_account_id.prefix()), + oracle_account_id.suffix().as_canonical_u64(), ); + // ------------------------------------------------------------------------- + // Get all foreign accounts for oracle data (BTC/USD pair) + // ------------------------------------------------------------------------- + let pair_word: Word = [ + Felt::new(PAIR_PREFIX), + Felt::new(PAIR_SUFFIX), + Felt::ZERO, + Felt::ZERO, + ] + .into(); + let foreign_accounts: Vec = + get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; + // ------------------------------------------------------------------------- // Create Oracle Reader contract // ------------------------------------------------------------------------- - // `include_str!` resolves at compile time relative to this source file, - // so the binary is independent of the working directory it is run from. - let contract_code = include_str!("../../../masm/accounts/oracle_reader.masm"); + // The oracle_reader masm file is a template; fill placeholders using the + // parsed oracle account ID, the pair encoding, and the deployment-specific + // procedure hash. + let contract_code: String = oracle_reader_source(oracle_account_id); + + // Defensive gate: if any placeholder was missed, fail loudly here rather + // than letting the MASM assembler emit an opaque parse error. The braces + // `{` / `}` only appear in oracle_reader.masm as template tokens. + if let Some(bad) = contract_code + .lines() + .find(|l| l.contains('{') || l.contains('}')) + { + panic!( + "oracle_reader template substitution incomplete; offending line: `{}`", + bad.trim() + ); + } let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); let contract_component_code = client .code_builder() - .compile_component_code("external_contract::oracle_reader", contract_code) + .compile_component_code("external_contract::oracle_reader", contract_code.as_str()) .unwrap(); let contract_component = AccountComponent::new( contract_component_code, @@ -201,19 +225,19 @@ async fn main() -> Result<(), ClientError> { // miden-vm#2778). let tx_script = client .code_builder() - .with_linked_module("external_contract::oracle_reader", contract_code) + .with_linked_module("external_contract::oracle_reader", contract_code.as_str()) .unwrap() .compile_tx_script(script_code) .unwrap(); - let tx_increment_request = TransactionRequestBuilder::new() + let tx_request = TransactionRequestBuilder::new() .foreign_accounts(foreign_accounts) .custom_script(tx_script) .build() .unwrap(); let tx_id = client - .submit_new_transaction(oracle_reader_contract.id(), tx_increment_request) + .submit_new_transaction(oracle_reader_contract.id(), tx_request) .await .unwrap(); From 12bc33279382b0832a0217c74abb44a6c5794a09 Mon Sep 17 00:00:00 2001 From: keinberger Date: Tue, 28 Apr 2026 16:03:46 +0300 Subject: [PATCH 3/5] fix: verify oracle template path locally and tighten Pragma re-verify wording Reorder oracle_data_query.rs to compile the oracle reader contract and tx script BEFORE the live Pragma storage walk, so a dead Pragma deployment no longer blocks local verification of the template substitution and MASM assembly path. Add three log lines (template substituted, component compiled, tx_script compiled) so the verification script can classify oracle import failure as TEMP PASS when the local compile path is healthy. Add oracle_tutorial.md to docs/src/lib.rs for doctesting alongside the other tutorial pages. Strengthen the Pragma callout in oracle_tutorial.md: after Pragma redeploys, the oracle account ID and GET_MEDIAN_PROC_HASH constant must be re-verified; do not assume either survives a redeploy unchanged. Apply prettier-formatting fixes to oracle_tutorial.md (italics syntax and three fenced-block language hints). --- docs/src/lib.rs | 1 + docs/src/rust-client/oracle_tutorial.md | 10 ++--- rust-client/src/bin/oracle_data_query.rs | 47 +++++++++++++++--------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 2cf4c50a..b70e3a76 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -9,3 +9,4 @@ #![doc = include_str!("rust-client/creating_notes_in_masm_tutorial.md")] #![doc = include_str!("rust-client/delegated_proving_tutorial.md")] #![doc = include_str!("rust-client/network_transactions_tutorial.md")] +#![doc = include_str!("rust-client/oracle_tutorial.md")] diff --git a/docs/src/rust-client/oracle_tutorial.md b/docs/src/rust-client/oracle_tutorial.md index 5d970f3f..5b54abaa 100644 --- a/docs/src/rust-client/oracle_tutorial.md +++ b/docs/src/rust-client/oracle_tutorial.md @@ -298,7 +298,7 @@ _Don't run this code just yet, we still need to create our smart contract that q The oracle account ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is encoded as `[PAIR_PREFIX = 1, PAIR_SUFFIX = 0, 0, 0]` per [Pragma's pair convention](https://github.com/astraly-labs/pragma-miden/blob/main/examples/consume-price/src/main.rs). The `get_oracle_foreign_accounts` function mirrors the walk in Pragma's `consume-price` example: read `pragma::oracle::next_publisher_index`, walk the `pragma::oracle::publishers` map for `i in 2..publisher_count` (Pragma reserves 0 and 1 as sentinels), then for each publisher request its `pragma::publisher::entries` map gated on the `pair_word`. The `trading_pair` requirement is therefore explicit in the function signature; passing a different `pair_word` reads a different price. :::note -The live oracle path is currently blocked pending a v0.14-compatible Pragma deployment. Pragma's source repo is on `miden-protocol` v0.13; the migration is tracked in [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40) (open PR). After Pragma migrates, the oracle account ID and the `get_median` procedure hash hardcoded in `oracle_data_query.rs` (constant `GET_MEDIAN_PROC_HASH`) must be re-verified against the new deployment. +The live oracle path is currently blocked pending a v0.14-compatible Pragma deployment. Pragma's source repo is on `miden-protocol` v0.13; the migration is tracked in [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40) (open PR). After Pragma migrates, the oracle account ID _and_ the `get_median` procedure hash hardcoded in `oracle_data_query.rs` (constant `GET_MEDIAN_PROC_HASH`) must be re-verified against the new deployment before the tutorial can be relied on. Both values are deployment-specific; do not assume they survive a redeploy unchanged. ::: ## Step 2: Build the price reader smart contract and script @@ -315,7 +315,7 @@ mkdir -p masm/accounts masm/scripts This will create: -``` +```text masm/ ├── accounts/ └── scripts/ @@ -387,15 +387,15 @@ end ## Step 3: Run the program -This tutorial requires a v0.14-compatible Pragma oracle deployment. The current Pragma deployment is on `miden-protocol` v0.13 (see [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40), open), so the live FPI walk will fail until that migration lands. The tutorial code itself is correct and will work the moment Pragma redeploys. Get the current testnet oracle account ID (bech32 or hex) from the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments) and pass it as a CLI argument: +This tutorial requires a v0.14-compatible Pragma oracle deployment. The current Pragma deployment is on `miden-protocol` v0.13 (see [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40), open), so the live FPI walk will fail until that migration lands. **After Pragma redeploys, you must re-verify the oracle account ID _and_ the `GET_MEDIAN_PROC_HASH` constant in `src/main.rs` against the new deployment** — both are deployment-specific and may change with each redeploy. Re-running the tutorial without re-verifying these constants is not safe. Get the current testnet oracle account ID (bech32 or hex) from the [astraly-labs/pragma-miden README](https://github.com/astraly-labs/pragma-miden#deployments) and pass it as a CLI argument: -``` +```bash cargo run --release -- ``` The output of our program will look something like this: -``` +```text cleared sqlite store: ./store.sqlite3 Latest block: 648397 Oracle accountId prefix: V0(AccountIdPrefixV0 { prefix: 5721796415433354752 }) suffix: 599064613630720 diff --git a/rust-client/src/bin/oracle_data_query.rs b/rust-client/src/bin/oracle_data_query.rs index 3b98a98e..f4ad51bb 100644 --- a/rust-client/src/bin/oracle_data_query.rs +++ b/rust-client/src/bin/oracle_data_query.rs @@ -149,24 +149,13 @@ async fn main() -> Result<(), ClientError> { ); // ------------------------------------------------------------------------- - // Get all foreign accounts for oracle data (BTC/USD pair) + // Build & locally compile the oracle reader contract + script // ------------------------------------------------------------------------- - let pair_word: Word = [ - Felt::new(PAIR_PREFIX), - Felt::new(PAIR_SUFFIX), - Felt::ZERO, - Felt::ZERO, - ] - .into(); - let foreign_accounts: Vec = - get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; - - // ------------------------------------------------------------------------- - // Create Oracle Reader contract - // ------------------------------------------------------------------------- - // The oracle_reader masm file is a template; fill placeholders using the - // parsed oracle account ID, the pair encoding, and the deployment-specific - // procedure hash. + // We do this BEFORE walking publishers so that a dead Pragma oracle does + // not block local verification of the template substitution and the MASM + // assembly. If anything below panics or fails to compile, the bug is in + // our code; if it succeeds and only the publisher walk fails, the bug is + // upstream (Pragma deployment availability). let contract_code: String = oracle_reader_source(oracle_account_id); // Defensive gate: if any placeholder was missed, fail loudly here rather @@ -181,6 +170,7 @@ async fn main() -> Result<(), ClientError> { bad.trim() ); } + println!("oracle_reader template substituted (no leftover placeholders)"); let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); @@ -188,6 +178,8 @@ async fn main() -> Result<(), ClientError> { .code_builder() .compile_component_code("external_contract::oracle_reader", contract_code.as_str()) .unwrap(); + println!("Compiled oracle_reader component locally"); + let contract_component = AccountComponent::new( contract_component_code, vec![StorageSlot::with_value( @@ -215,7 +207,7 @@ async fn main() -> Result<(), ClientError> { .unwrap(); // ------------------------------------------------------------------------- - // Build the script that calls our `get_price` procedure + // Compile the script that calls our `get_price` procedure // ------------------------------------------------------------------------- let script_code = include_str!("../../../masm/scripts/oracle_reader_script.masm"); @@ -229,6 +221,25 @@ async fn main() -> Result<(), ClientError> { .unwrap() .compile_tx_script(script_code) .unwrap(); + println!("Compiled tx_script with linked oracle_reader module"); + + // ------------------------------------------------------------------------- + // Walk Pragma's storage to import the oracle + publishers (BTC/USD pair) + // ------------------------------------------------------------------------- + // This is the FIRST step that depends on a live Pragma deployment. If + // Pragma is on a different miden-protocol version than this tutorial, or + // if Pragma's accounts have been redeployed without updating the IDs, this + // call will fail with `AccountNotFoundOnChain`. The local compile above + // proves the template + MASM path is healthy independent of that. + let pair_word: Word = [ + Felt::new(PAIR_PREFIX), + Felt::new(PAIR_SUFFIX), + Felt::ZERO, + Felt::ZERO, + ] + .into(); + let foreign_accounts: Vec = + get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; let tx_request = TransactionRequestBuilder::new() .foreign_accounts(foreign_accounts) From 16c929888bdd577e5dc88a07a55b1d2271dbf33e Mon Sep 17 00:00:00 2001 From: keinberger Date: Tue, 28 Apr 2026 17:11:29 +0300 Subject: [PATCH 4/5] fix: sync oracle tutorial doc snippet with runnable source order Reorder the doc snippet in oracle_tutorial.md to match the runnable oracle_data_query.rs: build and locally compile the oracle reader contract and tx script BEFORE walking Pragma's storage, with the same three progress println lines (template substituted, component compiled, tx_script compiled). The doc and bin now read identically through the local-compile path, so the tutorial accurately reflects how a dead Pragma deployment surfaces (only the storage walk fails). Replace stale 'trading_pair requirement' wording with 'pair_word' to match the actual function-signature argument. --- docs/src/rust-client/oracle_tutorial.md | 51 ++++++++++++++++--------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/docs/src/rust-client/oracle_tutorial.md b/docs/src/rust-client/oracle_tutorial.md index 5b54abaa..3c01c788 100644 --- a/docs/src/rust-client/oracle_tutorial.md +++ b/docs/src/rust-client/oracle_tutorial.md @@ -193,21 +193,14 @@ async fn main() -> Result<(), ClientError> { ); // ------------------------------------------------------------------------- - // Get all foreign accounts for oracle data (BTC/USD) - // ------------------------------------------------------------------------- - let pair_word: Word = [ - Felt::new(PAIR_PREFIX), - Felt::new(PAIR_SUFFIX), - Felt::ZERO, - Felt::ZERO, - ] - .into(); - let foreign_accounts: Vec = - get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; - - // ------------------------------------------------------------------------- - // Create Oracle Reader contract + // Build & locally compile the oracle reader contract + script // ------------------------------------------------------------------------- + // We do this BEFORE walking publishers so that a dead Pragma oracle does + // not block local verification of the template substitution and the MASM + // assembly. If anything below panics or fails to compile, the bug is in + // our code; if it succeeds and only the publisher walk fails, the bug is + // upstream (Pragma deployment availability). + // // `oracle_reader.masm` is a *template*: the placeholders `{pair_prefix}`, // `{pair_suffix}`, `{get_median_proc_hash}`, `{oracle_id_prefix}`, and // `{oracle_id_suffix}` are substituted by the Rust binary before the @@ -225,6 +218,7 @@ async fn main() -> Result<(), ClientError> { bad.trim() ); } + println!("oracle_reader template substituted (no leftover placeholders)"); let contract_slot_name = StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name"); @@ -232,6 +226,8 @@ async fn main() -> Result<(), ClientError> { .code_builder() .compile_component_code("external_contract::oracle_reader", contract_code.as_str()) .unwrap(); + println!("Compiled oracle_reader component locally"); + let contract_component = AccountComponent::new( contract_component_code, vec![StorageSlot::with_value(contract_slot_name.clone(), Word::default())], @@ -256,7 +252,7 @@ async fn main() -> Result<(), ClientError> { .unwrap(); // ------------------------------------------------------------------------- - // Build the script that calls our `get_price` procedure + // Compile the script that calls our `get_price` procedure // ------------------------------------------------------------------------- let script_code = include_str!("../masm/scripts/oracle_reader_script.masm"); @@ -270,15 +266,34 @@ async fn main() -> Result<(), ClientError> { .unwrap() .compile_tx_script(script_code) .unwrap(); + println!("Compiled tx_script with linked oracle_reader module"); + + // ------------------------------------------------------------------------- + // Walk Pragma's storage to import the oracle + publishers (BTC/USD pair) + // ------------------------------------------------------------------------- + // This is the FIRST step that depends on a live Pragma deployment. If + // Pragma is on a different miden-protocol version than this tutorial, or + // if Pragma's accounts have been redeployed without updating the IDs, this + // call will fail with `AccountNotFoundOnChain`. The local compile above + // proves the template + MASM path is healthy independent of that. + let pair_word: Word = [ + Felt::new(PAIR_PREFIX), + Felt::new(PAIR_SUFFIX), + Felt::ZERO, + Felt::ZERO, + ] + .into(); + let foreign_accounts: Vec = + get_oracle_foreign_accounts(&mut client, oracle_account_id, pair_word).await?; - let tx_increment_request = TransactionRequestBuilder::new() + let tx_request = TransactionRequestBuilder::new() .foreign_accounts(foreign_accounts) .custom_script(tx_script) .build() .unwrap(); let tx_id = client - .submit_new_transaction(oracle_reader_contract.id(), tx_increment_request) + .submit_new_transaction(oracle_reader_contract.id(), tx_request) .await .unwrap(); @@ -295,7 +310,7 @@ async fn main() -> Result<(), ClientError> { _Don't run this code just yet, we still need to create our smart contract that queries the oracle_ -The oracle account ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is encoded as `[PAIR_PREFIX = 1, PAIR_SUFFIX = 0, 0, 0]` per [Pragma's pair convention](https://github.com/astraly-labs/pragma-miden/blob/main/examples/consume-price/src/main.rs). The `get_oracle_foreign_accounts` function mirrors the walk in Pragma's `consume-price` example: read `pragma::oracle::next_publisher_index`, walk the `pragma::oracle::publishers` map for `i in 2..publisher_count` (Pragma reserves 0 and 1 as sentinels), then for each publisher request its `pragma::publisher::entries` map gated on the `pair_word`. The `trading_pair` requirement is therefore explicit in the function signature; passing a different `pair_word` reads a different price. +The oracle account ID is read from the first CLI argument (see "Running the tutorial" at the bottom of this page) and the BTC/USD pair is encoded as `[PAIR_PREFIX = 1, PAIR_SUFFIX = 0, 0, 0]` per [Pragma's pair convention](https://github.com/astraly-labs/pragma-miden/blob/main/examples/consume-price/src/main.rs). The `get_oracle_foreign_accounts` function mirrors the walk in Pragma's `consume-price` example: read `pragma::oracle::next_publisher_index`, walk the `pragma::oracle::publishers` map for `i in 2..publisher_count` (Pragma reserves 0 and 1 as sentinels), then for each publisher request its `pragma::publisher::entries` map gated on the `pair_word`. The `pair_word` requirement is therefore explicit in the function signature; passing a different `pair_word` reads a different price. :::note The live oracle path is currently blocked pending a v0.14-compatible Pragma deployment. Pragma's source repo is on `miden-protocol` v0.13; the migration is tracked in [astraly-labs/pragma-miden#40](https://github.com/astraly-labs/pragma-miden/pull/40) (open PR). After Pragma migrates, the oracle account ID _and_ the `get_median` procedure hash hardcoded in `oracle_data_query.rs` (constant `GET_MEDIAN_PROC_HASH`) must be re-verified against the new deployment before the tutorial can be relied on. Both values are deployment-specific; do not assume they survive a redeploy unchanged. From 5dfc61eca3b3b21c2e97753863ed32d5f90058af Mon Sep 17 00:00:00 2001 From: keinberger Date: Thu, 7 May 2026 11:55:28 +0200 Subject: [PATCH 5/5] docs: remove VM issue references from tutorial comments Address partylikeits1983 review on PR #192: drop `miden-vm#2778` and related internal-VM context from future-facing tutorial and example comments. Behavior is unchanged (the `client.code_builder() .with_linked_module(...)` path is preserved); only the rationale text in comments is reduced to the tutorial-relevant local fact (the contract code is linked into the same `CodeBuilder` chain that compiles the script or note). Affected: 6 rust-client binaries and 6 mirrored tutorial doc snippets. --- .../rust-client/counter_contract_tutorial.md | 18 ++++-------------- .../foreign_procedure_invocation_tutorial.md | 8 ++------ .../src/rust-client/mappings_in_masm_how_to.md | 3 +-- .../network_transactions_tutorial.md | 10 ++++------ docs/src/rust-client/oracle_tutorial.md | 4 +--- .../public_account_interaction_tutorial.md | 10 ++++------ rust-client/src/bin/counter_contract_deploy.rs | 9 ++------- rust-client/src/bin/counter_contract_fpi.rs | 4 +--- .../src/bin/counter_contract_increment.rs | 5 ++--- rust-client/src/bin/mapping_example.rs | 3 +-- .../src/bin/network_notes_counter_contract.rs | 5 ++--- rust-client/src/bin/oracle_data_query.rs | 4 +--- 12 files changed, 25 insertions(+), 58 deletions(-) diff --git a/docs/src/rust-client/counter_contract_tutorial.md b/docs/src/rust-client/counter_contract_tutorial.md index d7da6a6b..d09a4c95 100644 --- a/docs/src/rust-client/counter_contract_tutorial.md +++ b/docs/src/rust-client/counter_contract_tutorial.md @@ -232,9 +232,6 @@ println!("\n[STEP 1] Creating counter contract."); let counter_code = include_str!("../masm/accounts/counter.masm"); // Compile the account code into `AccountComponent` with one storage slot. -// Using `client.code_builder()` makes the assembler share the client's -// persisted source manager, which keeps debug spans coherent for any -// libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); let component_code = client @@ -301,10 +298,8 @@ println!("\n[STEP 2] Call Counter Contract With Script"); // Load the MASM script referencing the increment procedure let script_code = include_str!("../masm/scripts/counter_script.masm"); -// Compile the script with the counter contract code linked as a dynamic -// module on the same `CodeBuilder`. This shares the client's source -// manager between parsing and assembly, which is what miden-vm#2778 -// requires to avoid panics when debug spans are reported. +// Compile the script with the counter contract code linked as a module +// on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) @@ -408,9 +403,6 @@ async fn main() -> Result<(), ClientError> { let counter_code = include_str!("../masm/accounts/counter.masm"); // Compile the account code into `AccountComponent` with one storage slot. - // Using `client.code_builder()` makes the assembler share the client's - // persisted source manager, which keeps debug spans coherent for any - // libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); let component_code = client @@ -454,10 +446,8 @@ async fn main() -> Result<(), ClientError> { // Load the MASM script referencing the increment procedure let script_code = include_str!("../masm/scripts/counter_script.masm"); - // Compile the script with the counter contract code linked as a dynamic - // module on the same `CodeBuilder`. This shares the client's source - // manager between parsing and assembly, which is what miden-vm#2778 - // requires to avoid panics when debug spans are reported. + // Compile the script with the counter contract code linked as a module + // on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) diff --git a/docs/src/rust-client/foreign_procedure_invocation_tutorial.md b/docs/src/rust-client/foreign_procedure_invocation_tutorial.md index 23871ffb..e10cf495 100644 --- a/docs/src/rust-client/foreign_procedure_invocation_tutorial.md +++ b/docs/src/rust-client/foreign_procedure_invocation_tutorial.md @@ -313,9 +313,7 @@ let script_code = include_str!("../masm/scripts/reader_script.masm") ); // Link the count reader contract code into the same `CodeBuilder` chain -// that compiles the script, so the assembler shares the client's -// persisted source manager (avoids the source-span mismatch from -// miden-vm#2778). +// that compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::count_reader_contract", count_reader_code) @@ -542,9 +540,7 @@ async fn main() -> Result<(), ClientError> { ); // Link the count reader contract code into the same `CodeBuilder` chain - // that compiles the script, so the assembler shares the client's - // persisted source manager (avoids the source-span mismatch from - // miden-vm#2778). + // that compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::count_reader_contract", count_reader_code) diff --git a/docs/src/rust-client/mappings_in_masm_how_to.md b/docs/src/rust-client/mappings_in_masm_how_to.md index c9e37a21..5c2b9621 100644 --- a/docs/src/rust-client/mappings_in_masm_how_to.md +++ b/docs/src/rust-client/mappings_in_masm_how_to.md @@ -245,8 +245,7 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../masm/scripts/mapping_example_script.masm"); // Compile the transaction script with the account code linked as a - // module on the same `CodeBuilder` chain (avoids the source-span - // mismatch from miden-vm#2778). + // module on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("miden_by_example::mapping_example_contract", account_code) diff --git a/docs/src/rust-client/network_transactions_tutorial.md b/docs/src/rust-client/network_transactions_tutorial.md index 83993540..ec59e540 100644 --- a/docs/src/rust-client/network_transactions_tutorial.md +++ b/docs/src/rust-client/network_transactions_tutorial.md @@ -330,8 +330,7 @@ println!("\n[STEP 3] Deploy network counter smart contract"); let script_code = include_str!("../masm/scripts/counter_script.masm"); // Link the counter contract code into the same `CodeBuilder` chain that -// compiles the script, so the assembler shares the client's persisted -// source manager (avoids the source-span mismatch from miden-vm#2778). +// compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? @@ -377,7 +376,7 @@ let network_note_code = include_str!("../masm/notes/network_increment_note.masm" let serial_num = client.rng().draw_word(); // Compile the note script with the counter contract code linked as a -// module on the same `CodeBuilder` chain (avoids miden-vm#2778). +// module on the same `CodeBuilder` chain. let note_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? @@ -627,8 +626,7 @@ async fn main() -> Result<(), Box> { let script_code = include_str!("../masm/scripts/counter_script.masm"); // Link the counter contract code into the same `CodeBuilder` chain that - // compiles the script, so the assembler shares the client's persisted - // source manager (avoids the source-span mismatch from miden-vm#2778). + // compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? @@ -664,7 +662,7 @@ async fn main() -> Result<(), Box> { let serial_num = client.rng().draw_word(); // Compile the note script with the counter contract code linked as a - // module on the same `CodeBuilder` chain (avoids miden-vm#2778). + // module on the same `CodeBuilder` chain. let note_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? diff --git a/docs/src/rust-client/oracle_tutorial.md b/docs/src/rust-client/oracle_tutorial.md index 3c01c788..7d1de799 100644 --- a/docs/src/rust-client/oracle_tutorial.md +++ b/docs/src/rust-client/oracle_tutorial.md @@ -257,9 +257,7 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../masm/scripts/oracle_reader_script.masm"); // Link the oracle reader contract code into the same `CodeBuilder` chain - // that compiles the script, so the assembler shares the client's - // persisted source manager (avoids the source-span mismatch from - // miden-vm#2778). + // that compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::oracle_reader", contract_code.as_str()) diff --git a/docs/src/rust-client/public_account_interaction_tutorial.md b/docs/src/rust-client/public_account_interaction_tutorial.md index 0ffbd136..4f375fe9 100644 --- a/docs/src/rust-client/public_account_interaction_tutorial.md +++ b/docs/src/rust-client/public_account_interaction_tutorial.md @@ -220,9 +220,8 @@ println!("\n[STEP 2] Call the increment_count procedure in the counter contract" let script_code = include_str!("../masm/scripts/counter_script.masm"); let counter_code = include_str!("../masm/accounts/counter.masm"); -// Compile the script against the counter contract code via a single -// `CodeBuilder` chain so the assembler shares the client's persisted -// source manager (avoids the source-span mismatch from miden-vm#2778). +// Compile the script with the counter contract code linked as a module +// on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) @@ -339,9 +338,8 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../masm/scripts/counter_script.masm"); let counter_code = include_str!("../masm/accounts/counter.masm"); - // Compile the script against the counter contract code via a single - // `CodeBuilder` chain so the assembler shares the client's persisted - // source manager (avoids the source-span mismatch from miden-vm#2778). + // Compile the script with the counter contract code linked as a module + // on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) diff --git a/rust-client/src/bin/counter_contract_deploy.rs b/rust-client/src/bin/counter_contract_deploy.rs index 0ead348b..b8cab7c5 100644 --- a/rust-client/src/bin/counter_contract_deploy.rs +++ b/rust-client/src/bin/counter_contract_deploy.rs @@ -51,9 +51,6 @@ async fn main() -> Result<(), ClientError> { let counter_code = include_str!("../../../masm/accounts/counter.masm"); // Compile the account code into `AccountComponent` with one storage slot. - // Using `client.code_builder()` makes the assembler share the client's - // persisted source manager, which keeps debug spans coherent for any - // libraries that link against this code later (see miden-vm#2778). let counter_slot_name = StorageSlotName::new("miden::tutorials::counter").expect("valid slot name"); let component_code = client @@ -100,10 +97,8 @@ async fn main() -> Result<(), ClientError> { // Load the MASM script referencing the increment procedure let script_code = include_str!("../../../masm/scripts/counter_script.masm"); - // Compile the script with the counter contract code linked as a dynamic - // module on the same `CodeBuilder`. This shares the client's source - // manager between parsing and assembly, which is what miden-vm#2778 - // requires to avoid panics when debug spans are reported. + // Compile the script with the counter contract code linked as a module + // on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) diff --git a/rust-client/src/bin/counter_contract_fpi.rs b/rust-client/src/bin/counter_contract_fpi.rs index 4c2b09b6..6a2e044b 100644 --- a/rust-client/src/bin/counter_contract_fpi.rs +++ b/rust-client/src/bin/counter_contract_fpi.rs @@ -162,9 +162,7 @@ async fn main() -> Result<(), ClientError> { ); // Link the count reader contract code into the same `CodeBuilder` chain - // that compiles the script, so the assembler shares the client's - // persisted source manager (avoids the source-span mismatch from - // miden-vm#2778). + // that compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::count_reader_contract", count_reader_code) diff --git a/rust-client/src/bin/counter_contract_increment.rs b/rust-client/src/bin/counter_contract_increment.rs index 6f0415e4..4d065b29 100644 --- a/rust-client/src/bin/counter_contract_increment.rs +++ b/rust-client/src/bin/counter_contract_increment.rs @@ -68,9 +68,8 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../../../masm/scripts/counter_script.masm"); let counter_code = include_str!("../../../masm/accounts/counter.masm"); - // Compile the script against the counter contract code via a single - // `CodeBuilder` chain so the assembler shares the client's persisted - // source manager (avoids the source-span mismatch from miden-vm#2778). + // Compile the script with the counter contract code linked as a module + // on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code) diff --git a/rust-client/src/bin/mapping_example.rs b/rust-client/src/bin/mapping_example.rs index 9f9626b5..43ea8a9b 100644 --- a/rust-client/src/bin/mapping_example.rs +++ b/rust-client/src/bin/mapping_example.rs @@ -98,8 +98,7 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../../../masm/scripts/mapping_example_script.masm"); // Compile the transaction script with the account code linked as a - // module on the same `CodeBuilder` chain (avoids the source-span - // mismatch from miden-vm#2778). + // module on the same `CodeBuilder` chain. let tx_script = client .code_builder() .with_linked_module("miden_by_example::mapping_example_contract", account_code) diff --git a/rust-client/src/bin/network_notes_counter_contract.rs b/rust-client/src/bin/network_notes_counter_contract.rs index 40cd41b9..3330d333 100644 --- a/rust-client/src/bin/network_notes_counter_contract.rs +++ b/rust-client/src/bin/network_notes_counter_contract.rs @@ -165,8 +165,7 @@ async fn main() -> Result<(), Box> { let script_code = include_str!("../../../masm/scripts/counter_script.masm"); // Link the counter contract code into the same `CodeBuilder` chain that - // compiles the script, so the assembler shares the client's persisted - // source manager (avoids the source-span mismatch from miden-vm#2778). + // compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? @@ -202,7 +201,7 @@ async fn main() -> Result<(), Box> { let serial_num = client.rng().draw_word(); // Compile the note script with the counter contract code linked as a - // module on the same `CodeBuilder` chain (avoids miden-vm#2778). + // module on the same `CodeBuilder` chain. let note_script = client .code_builder() .with_linked_module("external_contract::counter_contract", counter_code)? diff --git a/rust-client/src/bin/oracle_data_query.rs b/rust-client/src/bin/oracle_data_query.rs index f4ad51bb..aaea928a 100644 --- a/rust-client/src/bin/oracle_data_query.rs +++ b/rust-client/src/bin/oracle_data_query.rs @@ -212,9 +212,7 @@ async fn main() -> Result<(), ClientError> { let script_code = include_str!("../../../masm/scripts/oracle_reader_script.masm"); // Link the oracle reader contract code into the same `CodeBuilder` chain - // that compiles the script, so the assembler shares the client's - // persisted source manager (avoids the source-span mismatch from - // miden-vm#2778). + // that compiles the script. let tx_script = client .code_builder() .with_linked_module("external_contract::oracle_reader", contract_code.as_str())