diff --git a/Cargo.lock b/Cargo.lock index 76ee2e7115..de46c1a555 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2191,7 +2191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3679,9 +3679,9 @@ dependencies = [ [[package]] name = "miden-agglayer" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e579ddb489e95caa827d1877f048ce53e5097459bc1c091ccec20f711c31fd3f" +checksum = "00c9ff4fee31e660b42ad8a5bb719fd52ce55d8177b4c57238f000e1314ee9e6" dependencies = [ "alloy-sol-types", "fs-err", @@ -3717,9 +3717,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2570665cc21c10d3b8681f5992070f2db91296db731cc0656ffeba9d26f84f5" +checksum = "42e7f7a3e02a31f1c5690b60c529eed11de3a897af84754c0a4b99a3152ca463" dependencies = [ "env_logger", "log", @@ -3735,9 +3735,9 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a563c327987a9fa9238b6bf9d286157e51c7818cb47a90e25c7e5c47b184b" +checksum = "37b3f2354462165f247c58c7c5216ba4f3938f673e9e0d6063ff0c4920137188" dependencies = [ "env_logger", "log", @@ -3789,9 +3789,9 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912411805cedecfb066bf2c67c68a41106b2a08816ffdd3bb432a2ac0d4ea20" +checksum = "27614510f8900914f65c40aab830591904fbf634013feeeca0306ab4f99b1242" dependencies = [ "miden-processor", "miden-protocol", @@ -3811,9 +3811,9 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "359ddf8ba29ab31924c9b71ff5ccf9b7c11c7aa0129a1e189aa4cd6f30b355dc" +checksum = "2e21b43b9d95efd36333bcc4dbf978a54d923b2904ed922480e336cf990a2737" dependencies = [ "derive_more", "log", @@ -3830,9 +3830,9 @@ dependencies = [ [[package]] name = "miden-core-lib" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf796fb15f11e9770273b6d87d498692e643e1fce817ea63e6eca42e3de4d48" +checksum = "559f199c9c6f827449f897cca48a1fbc766d7f2cc628a87a5a75dc1cb77ff06d" dependencies = [ "env_logger", "fs-err", @@ -3851,9 +3851,9 @@ dependencies = [ [[package]] name = "miden-core-lib-codegen" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fd10c163d02a2b7f945554075a1fda7558925f613812a45a5c00bf5449caea" +checksum = "4a317014a6749628ea1c32e0b3f3800d994a73c7ada643a22e5dfad9564e273a" dependencies = [ "miden-core", "miden-precompiles", @@ -3862,9 +3862,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba74f29ee9756631ed85794c0a15f5099a29758436526f57b607e1a1cadf293f" +checksum = "b4756c2e871ecd7d995a1a7d3064bf37f9a0115c4f5b1a51ab99cc15b1006b59" dependencies = [ "blake3", "cc", @@ -3905,9 +3905,9 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7578de2aff9443eab6852c12b7a0fbf12e937a0a27e92c33605a3df1abd8da2" +checksum = "5d2081b776d9bef188fcd3e41ebe8cae85fa19773e02b95370c89a45a703b08f" dependencies = [ "quote", "syn 2.0.119", @@ -4021,9 +4021,9 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443ecb138aceaad1b03a0952675ec53807a768f1b466ef0b68adc3b9363f84d0" +checksum = "8e376af0269a87f2b1fd82b24aeb723576bda9c63296d101618c7ce8f9982957" dependencies = [ "hashbrown 0.17.1", "miden-assembly-syntax", @@ -4467,21 +4467,24 @@ dependencies = [ [[package]] name = "miden-objects" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad8ade9bb1740dd09d9873fee7d51b9ea63d38aa0971b19455848681042d94e" +checksum = "54b2012b85dc292da2941d58637a89e400ed1bad732590e9e094edd01191c3d7" dependencies = [ + "miden-protobuf", "miden-protocol", "prost", "prost-build", "protox", + "quote", + "thiserror 2.0.20", ] [[package]] name = "miden-package-registry" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "800a99645d14dbbb59c7e43dd28305cf7a3af8c82aee1fa2724f67f6e54a5346" +checksum = "8444acad2bc21dc662a845d3fd57ccfb57b93abddeac3017e60781d15c89264a" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -4495,9 +4498,9 @@ dependencies = [ [[package]] name = "miden-precompiles" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d64ed605e6320c507ab620dda1edfeba66ef724b685d59b5424fc36d587bb3f0" +checksum = "c189dae844624cfb0edf8d12203febc17ae47434f51d825bf7019d8dff5f7acf" dependencies = [ "miden-core", "miden-crypto", @@ -4561,9 +4564,9 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f06ff53be95356c8fba4b573e813be52dc2d1c0877786d8c9d3929f5503dc9" +checksum = "b76c2477a17a83ac74a6956a2fec521b1f7a4847a617a318a6a4602fae856390" dependencies = [ "hashbrown 0.17.1", "itertools 0.15.0", @@ -4582,9 +4585,9 @@ dependencies = [ [[package]] name = "miden-project" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f504225b1cc724198ac7fccba5ec4c763054f2b538cbb984657eb6e785bf49f" +checksum = "4c576320a3dedda186b00c1429327d9f11d658c5986e0935bb4efedc76c864f8" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -4598,11 +4601,36 @@ dependencies = [ "toml", ] +[[package]] +name = "miden-protobuf" +version = "0.17.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9ad73d3b4049f8c06c00885db45549d78861b7aae21469ecae47e3555fb812" +dependencies = [ + "miden-protobuf-derive", + "proc-macro-crate", + "prost", + "prost-build", + "prost-types", +] + +[[package]] +name = "miden-protobuf-derive" +version = "0.17.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d7015ac0b8c159741faf1e2c41ee853cf2d4bd324ed87e03bd3f0dd9769500" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "miden-protocol" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2156f076ad20f1e271ed7fc7c404393ee206f44307c5684d7c43d1ab7923e20e" +checksum = "4a24e0a3e28f1e6cfa9043f0b937d5c85635b242ea73db6b78aed40e081e8965" dependencies = [ "bech32", "fs-err", @@ -4632,9 +4660,9 @@ dependencies = [ [[package]] name = "miden-protocol-build-utils" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88ff1be87aa49de41fdf45b3c52b5851270fc0293fa10b3993db0d6bbda49b3" +checksum = "d6a29158c8cbcbe6257930abbd3eb080285f8c320a97e862daa6c7f20d2112a5" dependencies = [ "fs-err", "miden-assembly", @@ -4648,9 +4676,9 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7f2af66603361338c69cfe4b44dcd3475b65fac947b8bbb5e1f113812a7daea" +checksum = "d9f27c87b47984f316fc5c9564a51392aaf6692a9e3e0d581a3bfc06e06fd5c4" dependencies = [ "miden-air", "miden-core", @@ -4714,9 +4742,9 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882ad707ef743ea4191c5d52b3cefecdeedeefe06da9047db9f9864724c54d42" +checksum = "a3fa91183c5da2732abf570db3b649e1985a8a1a42d192aa0125e08fad56c40e" dependencies = [ "bon", "miden-assembly", @@ -4753,9 +4781,9 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8485920dd5a087a861097dd0bb5930740b903334c188f5bc33f0d3dbabee88b4" +checksum = "2af5e9d292edd5092b55a3fd13921cde3ddbf9b4b633aaba8f0d0c0349a8a06c" dependencies = [ "anyhow", "itertools 0.15.0", @@ -4774,9 +4802,9 @@ dependencies = [ [[package]] name = "miden-tx" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a944c6c07c696a096df8edd654bd148234e11707302536047747232c1dca15" +checksum = "81af24672b850f520acba2bdb1e2f24fd1c74ff5ddbd57709b04e658d3b370ae" dependencies = [ "bon", "miden-agglayer", @@ -4789,9 +4817,9 @@ dependencies = [ [[package]] name = "miden-tx-batch" -version = "0.17.0-rc.3" +version = "0.17.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d63da10df2ac4f7830f0c360609326f728239f4814330e975707c134a9ea246" +checksum = "cdd4af823004cfc26203e2cf396b36de1bc9dd7b6576355ed74722b2a0f90456" dependencies = [ "miden-processor", "miden-protocol", @@ -4835,9 +4863,9 @@ dependencies = [ [[package]] name = "miden-utils-sync" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf61bdb8e94f0e9f7c59fcbf0c37faa092a98c2d19152377c1fc2044255bfd58" +checksum = "3f3d0f89c457b8357e6d6b5da329999bfd763a61ab7fe3a71b04f3e363912a26" dependencies = [ "lock_api", "loom", @@ -4891,9 +4919,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4e5cd6450f03cbda2cb7addec584acca088e97d38ea9855e2f9b35604870df" +checksum = "f5a615b3727fead78996884e4fa54bff951918e361955d09959cdab3ce88f232" dependencies = [ "miden-air", "miden-core", @@ -6003,7 +6031,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6467,7 +6495,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6526,7 +6554,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7201,7 +7229,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8145,7 +8173,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1c57cdb9fb..0d333de77f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,13 +58,13 @@ miden-node-tracing-macro = { path = "crates/tracing-macro", version = "0.17.0 miden-node-utils = { path = "crates/utils", version = "0.17.0-rc.1" } # miden-protocol dependencies. These should be updated in sync. -miden-block-prover = { version = "0.17.0-rc.3" } -miden-objects = { version = "0.17.0-rc.3" } -miden-protocol = { default-features = false, version = "0.17.0-rc.3" } -miden-standards = { version = "0.17.0-rc.3" } -miden-testing = { version = "0.17.0-rc.3" } -miden-tx = { default-features = false, version = "0.17.0-rc.3" } -miden-tx-batch = { version = "0.17.0-rc.3" } +miden-block-prover = { version = "0.17.0-rc.4" } +miden-objects = { version = "0.17.0-rc.4" } +miden-protocol = { default-features = false, version = "0.17.0-rc.4" } +miden-standards = { version = "0.17.0-rc.4" } +miden-testing = { version = "0.17.0-rc.4" } +miden-tx = { default-features = false, version = "0.17.0-rc.4" } +miden-tx-batch = { version = "0.17.0-rc.4" } # Other miden dependencies. These should align with those expected by miden-protocol. miden-crypto = { version = "0.32.0" } diff --git a/bin/benchmark/src/create_proofs.rs b/bin/benchmark/src/create_proofs.rs index d5a60fa657..e342eb06a4 100644 --- a/bin/benchmark/src/create_proofs.rs +++ b/bin/benchmark/src/create_proofs.rs @@ -12,6 +12,7 @@ use std::path::PathBuf; use std::sync::Arc; use std::time::{Duration, Instant}; +use miden_node_proto::{BuildUnchecked, DecodeMessage}; use miden_protocol::account::auth::{AuthScheme, AuthSecretKey}; use miden_protocol::account::{ Account, @@ -186,7 +187,8 @@ pub(crate) async fn run(rpc_url: Url, num_transactions: u64, remote_prover_url: .into_inner() .block_header .expect("RPC returned no block header"); - let genesis_header: BlockHeader = genesis_header_proto.try_into().unwrap(); + let genesis_header: BlockHeader = + genesis_header_proto.decode_fields().unwrap().build_unchecked().unwrap(); println!("Fetching chain tip state..."); let (ref_block_header, protocol_config, partial_blockchain) = fetch_chain_tip_state(&mut rpc_client, &genesis_header) diff --git a/bin/benchmark/src/inclusion.rs b/bin/benchmark/src/inclusion.rs index fe29292b93..708b0aa900 100644 --- a/bin/benchmark/src/inclusion.rs +++ b/bin/benchmark/src/inclusion.rs @@ -11,9 +11,9 @@ use std::collections::HashMap; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use miden_node_proto::clients::RpcClient; -use miden_node_proto::generated as proto; use miden_node_proto::generated::rpc::BlockHeaderByNumberRequest; -use miden_protocol::block::{BlockHeader, SignedBlock}; +use miden_node_proto::{BuildUnchecked, DecodeMessage, generated as proto}; +use miden_protocol::block::BlockHeader; use miden_protocol::transaction::TransactionId; /// One scanned block that contained at least one of our txs. Empty blocks in the scan range are not @@ -129,7 +129,11 @@ pub(crate) async fn scan_with_drain( next_block += 1; continue; }; - let signed_block = match SignedBlock::try_from(block) { + let signed_block = match block + .decode_fields() + .map_err(anyhow::Error::from) + .and_then(|block| block.build_unchecked().map_err(anyhow::Error::from)) + { Ok(sb) => sb, Err(err) => { eprintln!( @@ -223,7 +227,9 @@ pub(crate) async fn current_block_height(mut client: RpcClient) -> u32 { let header: BlockHeader = response .block_header .expect("no block header in response") - .try_into() - .expect("failed to decode block header"); + .decode_fields() + .expect("failed to decode block header") + .build_unchecked() + .expect("failed to build block header"); header.block_num().as_u32() } diff --git a/bin/benchmark/src/main.rs b/bin/benchmark/src/main.rs index 0c810ee21e..0abb8063ca 100644 --- a/bin/benchmark/src/main.rs +++ b/bin/benchmark/src/main.rs @@ -17,6 +17,7 @@ use miden_node_proto::domain::encryption::{ verify_transaction_encryption_key, }; use miden_node_proto::generated::rpc::BlockHeaderByNumberRequest; +use miden_node_proto::{BuildUnchecked, DecodeMessage}; use miden_protocol::Word; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::PublicKey as ValidatorPublicKey; @@ -177,8 +178,11 @@ async fn discover_genesis(rpc_url: &Url, timeout: Duration) -> Result { .block_header .ok_or_else(|| anyhow::anyhow!("No block header in response"))?; - let genesis_header: BlockHeader = - genesis_block_header.try_into().context("Failed to convert block header")?; + let genesis_header: BlockHeader = genesis_block_header + .decode_fields() + .context("Failed to decode block header")? + .build_unchecked() + .context("Failed to build block header")?; Ok(genesis_header.commitment()) } diff --git a/bin/benchmark/src/prover.rs b/bin/benchmark/src/prover.rs index 7ca386c168..b846faf0b5 100644 --- a/bin/benchmark/src/prover.rs +++ b/bin/benchmark/src/prover.rs @@ -18,6 +18,7 @@ use miden_node_proto::clients::{Builder, RemoteProverClient}; use miden_node_proto::generated::remote_prover::proof::Proof as ProofVariant; use miden_node_proto::generated::remote_prover::proof_request::Request; use miden_node_proto::generated::remote_prover::{Proof, ProofRequest}; +use miden_node_proto::{BuildUnchecked, DecodeMessage}; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_protocol::transaction::{ExecutedTransaction, ProvenTransaction, TransactionInputs}; use miden_tx::{LocalTransactionProver, TransactionProverError}; @@ -226,12 +227,21 @@ fn decode_transaction_proof(response: Proof) -> Result Result> { - let id_bytes: [u8; 15] = account_id.into(); let request = AccountRequest { - account_id: Some(ProtoAccountId { id: id_bytes.to_vec() }), + account_id: Some(account_id.into()), block_num: None, details: Some(AccountDetailRequest { code_commitment: None, @@ -186,12 +186,15 @@ impl SubmissionClient { .find(|slot| slot.slot_name == slot_name) .with_context(|| format!("account has no storage slot named '{slot_name}'"))?; - let value: Word = slot - .commitment - .as_ref() - .context("storage slot carries no value")? - .try_into() - .context("failed to decode the storage slot value")?; + let value: Word = match slot.content.as_ref() { + Some(SlotContent::Value(value)) => { + value.try_into().context("failed to decode the storage slot value")? + }, + Some(SlotContent::MapRoot(_)) => { + anyhow::bail!("storage slot '{slot_name}' is a storage map") + }, + None => anyhow::bail!("storage slot carries no value"), + }; // A value slot holds the number in the word's first element. Ok(Some( @@ -213,9 +216,8 @@ impl SubmissionClient { account_id: AccountId, block_num: BlockNumber, ) -> Result { - let id_bytes: [u8; 15] = account_id.into(); let request = AccountRequest { - account_id: Some(ProtoAccountId { id: id_bytes.to_vec() }), + account_id: Some(account_id.into()), block_num: Some(block_num.into()), details: None, }; @@ -337,8 +339,10 @@ fn decode_genesis_block_state( let header = response .block_header .context("RPC returned no genesis block header")? - .try_into() - .context("failed to decode the genesis block header")?; + .decode_fields() + .context("failed to decode the genesis block header")? + .build_unchecked() + .context("failed to build the genesis block header")?; let protocol_config = ensure_protocol_config_is_present_and_matches_header(response.protocol_config, &header) .context("RPC returned no valid genesis protocol configuration")?; diff --git a/bin/network-monitor/src/counter.rs b/bin/network-monitor/src/counter.rs index 1c0c3d7656..1c1a674923 100644 --- a/bin/network-monitor/src/counter.rs +++ b/bin/network-monitor/src/counter.rs @@ -9,6 +9,8 @@ use std::time::{Duration, Instant}; use anyhow::{Context, Result}; use miden_node_proto::clients::RpcClient; +use miden_node_proto::generated::account::account_storage_header::storage_slot::Content as SlotContent; +use miden_node_proto::{DecodeMessage, Verify}; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_node_tracing::{debug, error, info, miden_instrument, warn}; use miden_protocol::account::auth::AuthSecretKey; @@ -1011,12 +1013,13 @@ async fn fetch_slot_value( .find(|slot| slot.slot_name == slot_name) .context(format!("slot '{slot_name}' not found"))?; - let slot_value: Word = slot - .commitment - .as_ref() - .context("missing storage slot value")? - .try_into() - .context("failed to convert slot value to word")?; + let slot_value: Word = match slot.content.as_ref() { + Some(SlotContent::Value(value)) => { + value.try_into().context("failed to convert slot value to word")? + }, + Some(SlotContent::MapRoot(_)) => anyhow::bail!("slot '{slot_name}' is a storage map"), + None => anyhow::bail!("missing storage slot value"), + }; let value = slot_value .as_elements() @@ -1035,9 +1038,7 @@ fn build_account_request( account_id: AccountId, include_code_and_vault: bool, ) -> miden_node_proto::generated::rpc::AccountRequest { - let id_bytes: [u8; 15] = account_id.into(); - let account_id_proto = - miden_node_proto::generated::account::AccountId { id: id_bytes.to_vec() }; + let account_id_proto: miden_node_proto::generated::account::AccountId = account_id.into(); let (code_commitment, asset_vault_commitment) = if include_code_and_vault { let dummy: miden_node_proto::generated::primitives::Word = Word::default().into(); @@ -1097,8 +1098,10 @@ async fn fetch_wallet_account( let code: AccountCode = details .code .context("server did not return account code")? - .try_into() - .context("failed to decode account code")?; + .decode_fields() + .context("failed to decode account code")? + .verify() + .context("failed to verify account code")?; let vault = match details.vault_details { Some(vault_details) if vault_details.too_many_assets => { @@ -1108,7 +1111,12 @@ async fn fetch_wallet_account( let assets: Vec = vault_details .assets .into_iter() - .map(TryInto::try_into) + .map(|asset| { + asset + .decode_fields() + .map_err(anyhow::Error::from) + .and_then(|asset| asset.verify().map_err(anyhow::Error::from)) + }) .collect::>() .context("failed to convert assets")?; AssetVault::new(&assets).context("failed to create vault")? @@ -1177,17 +1185,13 @@ fn build_account_storage( for slot in storage_header.slots { let slot_name = miden_protocol::account::StorageSlotName::new(slot.slot_name.clone()) .context("invalid slot name")?; - let value: Word = slot - .commitment - .context("missing slot value")? - .try_into() - .context("invalid slot value")?; - - // slot_type: 0 = Value, 1 = Map - anyhow::ensure!( - slot.slot_type == 0, - "storage map slots are not supported for this account" - ); + let value: Word = match slot.content { + Some(SlotContent::Value(value)) => value.try_into().context("invalid slot value")?, + Some(SlotContent::MapRoot(_)) => { + anyhow::bail!("storage map slots are not supported for this account") + }, + None => anyhow::bail!("missing slot value"), + }; slots.push(StorageSlot::with_value(slot_name, value)); } diff --git a/bin/network-monitor/src/deploy/mod.rs b/bin/network-monitor/src/deploy/mod.rs index dbce4eecb2..d71c3559b6 100644 --- a/bin/network-monitor/src/deploy/mod.rs +++ b/bin/network-monitor/src/deploy/mod.rs @@ -25,6 +25,7 @@ use miden_node_proto::generated::rpc::{ SyncChainMmrResponse, }; use miden_node_proto::generated::submission::ProvenTransactionSubmission as ProtoProvenTransaction; +use miden_node_proto::{BuildUnchecked, DecodeMessage, Verify}; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_node_tracing::{debug, info, miden_instrument, warn}; use miden_node_utils::retry; @@ -282,8 +283,11 @@ pub async fn create_genesis_aware_rpc_client( .block_header .ok_or_else(|| anyhow::anyhow!("No block header in response"))?; - let genesis_header: BlockHeader = - genesis_block_header.try_into().context("Failed to convert block header")?; + let genesis_header: BlockHeader = genesis_block_header + .decode_fields() + .context("failed to decode block header")? + .build_unchecked() + .context("failed to build block header")?; let genesis_commitment = genesis_header.commitment(); // Rebuild the client, this time including the required genesis metadata so that write RPCs // like SubmitProvenTx are accepted by the node. @@ -456,11 +460,10 @@ pub(crate) async fn fetch_foreign_account_inputs( use miden_node_proto::generated::rpc::account_request::AccountDetailRequest; use miden_node_proto::generated::rpc::account_request::account_detail_request::StorageRequest; - let id_bytes: [u8; 15] = account_id.into(); // Dummy commitments force the server to include code and vault data in the response. let dummy: miden_node_proto::generated::primitives::Word = Word::default().into(); let request = ProtoAccountRequest { - account_id: Some(miden_node_proto::generated::account::AccountId { id: id_bytes.to_vec() }), + account_id: Some(account_id.into()), block_num: Some(block_num.into()), details: Some(AccountDetailRequest { code_commitment: Some(dummy.clone()), @@ -741,8 +744,10 @@ fn decode_chain_state( let tip_header: BlockHeader = response .block_header .context("sync_chain_mmr response did not include a block header")? - .try_into() - .context("failed to convert the sync target block header")?; + .decode_fields() + .context("failed to decode the sync target block header")? + .build_unchecked() + .context("failed to build the sync target block header")?; let protocol_config = ensure_protocol_config_is_present_and_matches_header(response.protocol_config, &tip_header) @@ -751,8 +756,10 @@ fn decode_chain_state( let delta: MmrDelta = response .mmr_delta .context("sync_chain_mmr response did not include an MMR delta")? - .try_into() - .context("failed to convert the MMR delta")?; + .decode_fields() + .context("failed to decode the MMR delta")? + .verify() + .context("failed to verify the MMR delta")?; let mut mmr = PartialMmr::from_peaks( MmrPeaks::new(Forest::new(0).context("empty forest should be valid")?, Vec::new()) diff --git a/bin/network-monitor/src/funding.rs b/bin/network-monitor/src/funding.rs index b526a379a1..50a9878b9f 100644 --- a/bin/network-monitor/src/funding.rs +++ b/bin/network-monitor/src/funding.rs @@ -11,6 +11,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use miden_node_proto::clients::RpcClient; use miden_node_proto::generated::rpc::NotesByIdRequest; +use miden_node_proto::{DecodeMessage, Verify}; use miden_node_tracing::{info, warn}; use miden_protocol::account::AccountId; use miden_protocol::note::{Note, NoteId}; @@ -236,8 +237,10 @@ async fn fetch_note(rpc_client: &mut RpcClient, note_id: NoteId) -> Result Result Result { // Only request account code let request = proto::rpc::AccountRequest { - account_id: Some(proto::account::AccountId { id: account_id.to_bytes() }), + account_id: Some(account_id.into()), block_num: Some(block_num.into()), // TODO: should these commitments be cached on the NTX builder? details: Some(proto::rpc::account_request::AccountDetailRequest { @@ -575,7 +582,7 @@ impl RpcClient { } let request = proto::rpc::AccountRequest { - account_id: Some(proto::account::AccountId { id: account_id.to_bytes() }), + account_id: Some(account_id.into()), block_num: block_num.map(Into::into), details: Some(proto::rpc::account_request::AccountDetailRequest { code_commitment: None, @@ -613,7 +620,7 @@ impl RpcClient { block_num: Option, ) -> Result { let request = proto::rpc::AccountRequest { - account_id: Some(proto::account::AccountId { id: account_id.to_bytes() }), + account_id: Some(account_id.into()), block_num: block_num.map(Into::into), details: Some(proto::rpc::account_request::AccountDetailRequest { code_commitment: None, @@ -689,9 +696,15 @@ impl RpcClient { .script; script - .map(NoteScript::try_from) + .map(|script| { + script + .decode_fields() + .map_err(ConversionError::from)? + .verify() + .map_err(ConversionError::new) + }) .transpose() - .map_err(|err| RpcError::Conversion(err.into())) + .map_err(RpcError::Conversion) } /// Issues a `GetAccount` request and decodes the response into the domain [`AccountResponse`]. diff --git a/bin/ntx-builder/src/db/queries/available_notes/mod.rs b/bin/ntx-builder/src/db/queries/available_notes/mod.rs index f9a35c3a5c..3ac11541c0 100644 --- a/bin/ntx-builder/src/db/queries/available_notes/mod.rs +++ b/bin/ntx-builder/src/db/queries/available_notes/mod.rs @@ -149,7 +149,7 @@ fn note_recheck_block( /// leaving the caller's next-block default. fn hint_next_consumable_block(hint: NoteExecutionHint, from: BlockNumber) -> Option { match hint { - NoteExecutionHint::None | NoteExecutionHint::Always => None, + NoteExecutionHint::None | NoteExecutionHint::Always | NoteExecutionHint::Unknown(_) => None, NoteExecutionHint::AfterBlock { block_num } => Some(block_num), NoteExecutionHint::OnBlockSlot { round_len, slot_len, slot_offset } => { let block = u64::from(from.as_u32()); diff --git a/bin/remote-prover/src/server/prover.rs b/bin/remote-prover/src/server/prover.rs index c58d965575..633fbf2331 100644 --- a/bin/remote-prover/src/server/prover.rs +++ b/bin/remote-prover/src/server/prover.rs @@ -4,7 +4,7 @@ use miden_node_proto::generated::remote_prover::proof::Proof as ProofVariant; use miden_node_proto::generated::remote_prover::proof_request::Request; use miden_node_proto::generated::{block_proving, remote_prover as proto, transaction}; use miden_node_tracing::{ErrorReport, miden_instrument}; -use miden_objects::conversion::decode_proposed_batch; +use miden_objects::{BuildUnchecked, DecodeMessage, VerifyWith}; use miden_protocol::MIN_PROOF_SECURITY_LEVEL; use miden_protocol::block::ProposedBlock; use miden_protocol::transaction::TransactionInputs; @@ -68,11 +68,19 @@ fn prove_transaction( prover: &LocalTransactionProver, input: transaction::TransactionInputs, ) -> Result { - let input = TransactionInputs::try_from(input).map_err(|error| { - tonic::Status::invalid_argument( - error.as_report_context("failed to decode transaction inputs"), - ) - })?; + let input: TransactionInputs = input + .decode_fields() + .map_err(|error| { + tonic::Status::invalid_argument( + error.as_report_context("failed to decode transaction inputs"), + ) + })? + .build_unchecked() + .map_err(|error| { + tonic::Status::invalid_argument( + error.as_report_context("failed to build transaction inputs"), + ) + })?; let transaction = prover.prove(input).map_err(|error| { tonic::Status::internal(error.as_report_context("failed to prove transaction")) })?; @@ -84,9 +92,19 @@ fn prove_batch( prover: &LocalBatchProver, input: transaction::ProposedBatch, ) -> Result { - let input = decode_proposed_batch(input, MIN_PROOF_SECURITY_LEVEL).map_err(|error| { - tonic::Status::invalid_argument(error.as_report_context("failed to decode proposed batch")) - })?; + let input = input + .decode_fields() + .map_err(|error| { + tonic::Status::invalid_argument( + error.as_report_context("failed to decode proposed batch"), + ) + })? + .verify_with(MIN_PROOF_SECURITY_LEVEL) + .map_err(|error| { + tonic::Status::invalid_argument( + error.as_report_context("failed to verify proposed batch"), + ) + })?; let executed_batch = BatchExecutor::new().execute(input).map_err(|error| { tonic::Status::internal(error.as_report_context("failed to execute batch")) })?; diff --git a/bin/remote-prover/src/server/tests.rs b/bin/remote-prover/src/server/tests.rs index 80fdba1d59..49c683bb8e 100644 --- a/bin/remote-prover/src/server/tests.rs +++ b/bin/remote-prover/src/server/tests.rs @@ -10,6 +10,7 @@ use miden_node_proto::generated::remote_prover::proof::Proof as ProofVariant; use miden_node_proto::generated::remote_prover::proof_request::Request; use miden_node_proto::generated::remote_prover::{Proof, ProofRequest}; use miden_node_utils::shutdown::CancellationToken; +use miden_objects::{BuildUnchecked, DecodeMessage, VerifyWith}; use miden_protocol::MIN_PROOF_SECURITY_LEVEL; use miden_protocol::account::auth::AuthScheme; use miden_protocol::asset::{Asset, FungibleAsset}; @@ -17,12 +18,7 @@ use miden_protocol::batch::{OrderedBatches, ProposedBatch}; use miden_protocol::block::{BlockHeader, BlockInputs, ProposedBlock}; use miden_protocol::note::NoteType; use miden_protocol::testing::account_id::{ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_SENDER}; -use miden_protocol::transaction::{ - ExecutedTransaction, - PartialBlockchain, - ProvenTransaction, - TransactionVerifier, -}; +use miden_protocol::transaction::{ExecutedTransaction, PartialBlockchain, TransactionVerifier}; use miden_protocol::vm::ExecutionProof; use miden_testing::{Auth, MockChainBuilder}; use miden_tx::LocalTransactionProver; @@ -444,7 +440,9 @@ async fn transaction_proof_is_correct() { let mut client = Client::connect(port).await; let response = client.submit_request(request).await.unwrap(); let response = match response.proof.unwrap() { - ProofVariant::Transaction(transaction) => ProvenTransaction::try_from(transaction).unwrap(), + ProofVariant::Transaction(transaction) => { + transaction.decode_fields().unwrap().build_unchecked().unwrap() + }, other => panic!("expected transaction proof response, got {other:?}"), }; @@ -472,9 +470,7 @@ async fn batch_proof_is_correct() { let mut client = Client::connect(port).await; let response = client.submit_request(request).await.unwrap(); let response = match response.proof.unwrap() { - ProofVariant::Batch(proof) => { - miden_objects::conversion::decode_proven_batch(proof, &batch).unwrap() - }, + ProofVariant::Batch(proof) => proof.decode_fields().unwrap().verify_with(&batch).unwrap(), other => panic!("expected batch proof response, got {other:?}"), }; diff --git a/bin/stress-test/src/store/mod.rs b/bin/stress-test/src/store/mod.rs index 0f25b3e9d7..ccc1c00ad9 100644 --- a/bin/stress-test/src/store/mod.rs +++ b/bin/stress-test/src/store/mod.rs @@ -11,7 +11,6 @@ use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; use miden_protocol::note::NoteTag; -use miden_protocol::utils::serde::Serializable; use rand::RngExt; use rand::seq::SliceRandom; use tokio::fs; @@ -174,7 +173,7 @@ fn get_account_request( }; proto::rpc::AccountRequest { - account_id: Some(proto::account::AccountId { id: account_id.to_bytes() }), + account_id: Some(account_id.into()), block_num: None, details: Some(AccountDetailRequest { code_commitment: None, diff --git a/bin/validator/src/server/validator_service/submit_proven_transaction.rs b/bin/validator/src/server/validator_service/submit_proven_transaction.rs index 37bb52fc28..ee46b4e200 100644 --- a/bin/validator/src/server/validator_service/submit_proven_transaction.rs +++ b/bin/validator/src/server/validator_service/submit_proven_transaction.rs @@ -1,7 +1,7 @@ use std::sync::atomic::Ordering; use miden_node_proto::domain::encryption::transaction_inputs_associated_data; -use miden_node_proto::generated as grpc; +use miden_node_proto::{BuildUnchecked, DecodeMessage, generated as grpc}; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_node_tracing::{ErrorReport, Instrument, info_span, miden_instrument, miden_span_record}; use miden_protocol::transaction::{ProvenTransaction, TransactionId, TransactionInputs}; @@ -102,9 +102,15 @@ impl grpc::server::validator_api::SubmitProvenTransaction for ValidatorService { let transaction = request .transaction .ok_or_else(|| Status::invalid_argument("Missing proven transaction"))?; - let tx = ProvenTransaction::try_from(transaction).map_err(|err| { - Status::invalid_argument(err.as_report_context("Invalid proven transaction")) - })?; + let tx: ProvenTransaction = transaction + .decode_fields() + .map_err(|err| { + Status::invalid_argument(err.as_report_context("Invalid proven transaction")) + })? + .build_unchecked() + .map_err(|err| { + Status::invalid_argument(err.as_report_context("Invalid proven transaction")) + })?; let sealed = request.sealed_transaction_inputs.ok_or_else(|| { Status::invalid_argument( "Missing sealed transaction inputs: fetch the encryption key with \ diff --git a/bin/validator/src/server/validator_service/tests.rs b/bin/validator/src/server/validator_service/tests.rs index 92029d59b3..b5828d4c1c 100644 --- a/bin/validator/src/server/validator_service/tests.rs +++ b/bin/validator/src/server/validator_service/tests.rs @@ -1,6 +1,5 @@ use std::collections::BTreeMap; -use miden_node_proto::SignBlockRequest; use miden_node_proto::domain::encryption::{ TransactionEncryptionScheme, TrustedTransactionEncryptionState, @@ -9,6 +8,7 @@ use miden_node_proto::domain::encryption::{ }; use miden_node_proto::generated::{self as proto}; use miden_node_proto::server::validator_api; +use miden_node_proto::{BuildUnchecked, DecodeMessage, SignBlockRequest, Verify}; use miden_node_store::{BlockStore, GenesisState}; use miden_node_utils::fee::{test_fee_params, test_protocol_config}; use miden_node_utils::testing::{ @@ -499,9 +499,9 @@ async fn sign_block_returns_signed_commitment() { "returned commitment must match the proposed block's commitment", ); let signature: miden_protocol::crypto::dsa::ecdsa_k256_keccak::Signature = - response.signature.unwrap().try_into().unwrap(); + response.signature.unwrap().decode_fields().unwrap().verify().unwrap(); let public_key: miden_protocol::crypto::dsa::ecdsa_k256_keccak::PublicKey = - response.public_key.unwrap().try_into().unwrap(); + response.public_key.unwrap().decode_fields().unwrap().verify().unwrap(); assert_eq!(public_key, tv.server.signer.public_key()); assert!(signature.verify(header.commitment(), &public_key)); } @@ -926,7 +926,12 @@ async fn block_subscription_replays_then_freezes_signing() { .expect("replayed block should arrive promptly") .expect("stream should not end") .expect("stream item should not be an error"); - let block = SignedBlock::try_from(response.block.expect("response should carry a block")) + let block: SignedBlock = response + .block + .expect("response should carry a block") + .decode_fields() + .expect("valid signed block") + .build_unchecked() .expect("valid signed block"); assert_eq!(block.header().block_num().as_u32(), expected); assert_eq!(response.committed_chain_tip, 2); @@ -934,7 +939,9 @@ async fn block_subscription_replays_then_freezes_signing() { let config: ProtocolConfig = response .protocol_config .expect("the first response must carry the active protocol config") - .try_into() + .decode_fields() + .unwrap() + .verify() .unwrap(); assert_eq!(config, tv.protocol_config); } else { @@ -1031,14 +1038,16 @@ async fn protocol_config_transition_is_streamed_and_used_for_next_signature() { .unwrap() .unwrap() .unwrap(); - let first_config: ProtocolConfig = first.protocol_config.unwrap().try_into().unwrap(); + let first_config: ProtocolConfig = + first.protocol_config.unwrap().decode_fields().unwrap().verify().unwrap(); assert_eq!(first_config, tv.protocol_config); let transition = tokio::time::timeout(Duration::from_secs(5), stream.next()) .await .unwrap() .unwrap() .unwrap(); - let streamed_config: ProtocolConfig = transition.protocol_config.unwrap().try_into().unwrap(); + let streamed_config: ProtocolConfig = + transition.protocol_config.unwrap().decode_fields().unwrap().verify().unwrap(); assert_eq!(streamed_config, next_config); drop(stream); diff --git a/crates/block-producer/src/batch_builder/remote_prover.rs b/crates/block-producer/src/batch_builder/remote_prover.rs index e6a0c88573..696a90c9af 100644 --- a/crates/block-producer/src/batch_builder/remote_prover.rs +++ b/crates/block-producer/src/batch_builder/remote_prover.rs @@ -2,7 +2,7 @@ use miden_node_proto::clients::{Builder, RemoteProverClient}; use miden_node_proto::generated::remote_prover::proof::Proof as ProofVariant; use miden_node_proto::generated::remote_prover::proof_request::Request; use miden_node_proto::generated::remote_prover::{Proof, ProofRequest}; -use miden_objects::conversion::decode_proven_batch; +use miden_objects::{DecodeMessage, VerifyWith}; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_tx_batch::LocalBatchProver; use url::Url; @@ -83,7 +83,12 @@ impl RemoteBatchProver { let response = self.client.clone().prove(request).await.map_err(RemoteProverError::Grpc)?; let proof = extract_batch_proof(response.into_inner())?; - decode_proven_batch(proof, &proposed_batch).map_err(RemoteProverError::Conversion) + proof + .decode_fields() + .and_then(|proof| { + proof.verify_with(&proposed_batch).map_err(miden_objects::ConversionError::new) + }) + .map_err(RemoteProverError::Conversion) } } diff --git a/crates/block-producer/src/domain/transaction.rs b/crates/block-producer/src/domain/transaction.rs index 2e652eb720..092df1be10 100644 --- a/crates/block-producer/src/domain/transaction.rs +++ b/crates/block-producer/src/domain/transaction.rs @@ -1,8 +1,10 @@ use std::collections::HashSet; use std::sync::Arc; +use miden_node_proto::decode::ConversionResultExt; use miden_node_proto::errors::ConversionError; use miden_node_proto::generated::sequencer; +use miden_objects::{BuildUnchecked, DecodeMessage}; use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::{BlockNumber, FeeParameters}; @@ -194,8 +196,11 @@ impl TryFrom for AuthenticatedTransaction { .ok_or_else(|| { ConversionError::missing_field::("transaction") })? - .try_into() - .map_err(ConversionError::from)?; + .decode_fields() + .context("transaction")? + .build_unchecked() + .map_err(ConversionError::new) + .context("transaction")?; let store_account_state = value.store_account_state.map(Word::try_from).transpose()?; diff --git a/crates/block-producer/src/rpc_sync.rs b/crates/block-producer/src/rpc_sync.rs index ac03cc3f6e..2f28f92a03 100644 --- a/crates/block-producer/src/rpc_sync.rs +++ b/crates/block-producer/src/rpc_sync.rs @@ -10,6 +10,7 @@ use miden_node_tracing::{Instrument, debug, info, info_span, miden_instrument, w use miden_node_utils::retry::{self, RetryableWithContext}; use miden_node_utils::shutdown::CancellationToken; use miden_node_utils::tasks::Tasks; +use miden_objects::{BuildUnchecked, DecodeMessage}; use miden_protocol::block::{BlockNumber, SignedBlock}; use miden_protocol::vm::ExecutionProof; use tokio_stream::StreamExt; @@ -234,8 +235,10 @@ impl BlockSync { let block: SignedBlock = event .block .ok_or_else(|| anyhow::anyhow!("upstream block event is missing its block"))? - .try_into() - .context("failed to decode block from upstream")?; + .decode_fields() + .context("failed to decode block from upstream")? + .build_unchecked() + .context("failed to build block from upstream")?; let protocol_config = event .protocol_config .map(|config| { diff --git a/crates/block-producer/src/store/mod.rs b/crates/block-producer/src/store/mod.rs index f89e134d2d..f23ace315c 100644 --- a/crates/block-producer/src/store/mod.rs +++ b/crates/block-producer/src/store/mod.rs @@ -3,10 +3,10 @@ use std::fmt::{Display, Formatter}; use std::num::NonZeroU32; use itertools::Itertools; -use miden_node_proto::decode; use miden_node_proto::decode::GrpcDecodeExt; use miden_node_proto::errors::ConversionError; use miden_node_proto::generated::sequencer; +use miden_node_proto::{decode, verify}; use miden_node_store::state::{State, TransactionInputs as StoreTransactionInputs}; use miden_node_tracing::{debug, miden_instrument}; use miden_node_utils::formatting::format_opt; @@ -100,7 +100,7 @@ impl TryFrom for TransactionInputs { fn try_from(value: sequencer::AuthInputs) -> Result { let decoder = value.decoder(); - let account_id = decode!(decoder, value.account_id)?; + let account_id = verify!(decoder, value.account_id)?; let account_commitment = value.account_commitment.map(Word::try_from).transpose()?; diff --git a/crates/block-producer/src/validator/mod.rs b/crates/block-producer/src/validator/mod.rs index ea2f28ee14..76f5cc3e5a 100644 --- a/crates/block-producer/src/validator/mod.rs +++ b/crates/block-producer/src/validator/mod.rs @@ -3,7 +3,7 @@ use std::time::Duration; use miden_node_proto::clients::{Builder, ValidatorClient}; use miden_node_proto::decode::GrpcDecodeExt; use miden_node_proto::errors::ConversionError; -use miden_node_proto::{decode, generated as proto}; +use miden_node_proto::{decode, generated as proto, verify}; use miden_node_tracing::{info, miden_instrument}; use miden_protocol::Word; use miden_protocol::block::{BlockInputs, ProposedBlock}; @@ -123,9 +123,9 @@ impl BlockProducerValidatorClient { response: proto::validator::SignBlockResponse, ) -> Result { let decoder = response.decoder(); - let signature: Signature = decode!(decoder, response.signature)?; + let signature: Signature = verify!(decoder, response.signature)?; let block_commitment = decode!(decoder, response.block_commitment)?; - let public_key = decode!(decoder, response.public_key)?; + let public_key = verify!(decoder, response.public_key)?; Ok(SignBlockResponse { signature, block_commitment, public_key }) } diff --git a/crates/proto/src/decode/mod.rs b/crates/proto/src/decode/mod.rs index 8001210bbf..39dc06ff86 100644 --- a/crates/proto/src/decode/mod.rs +++ b/crates/proto/src/decode/mod.rs @@ -1,5 +1,7 @@ use std::marker::PhantomData; +use miden_objects::{DecodeMessage, Verify}; + mod utils; pub use utils::*; @@ -53,6 +55,60 @@ impl GrpcStructDecoder { .try_into() .context(name) } + + /// Decode a required optional field and verify the domain invariants. + /// + /// The decode step checks the wire representation. The verify step checks the domain + /// invariants. Both steps add the field name to the error path. + /// + /// A type that needs external context to verify, or that only supports unchecked + /// construction, does not satisfy the `Verify` bound. Decode such a field at the call site. + pub fn verify_field( + &self, + name: &'static str, + value: Option, + ) -> Result + where + T: DecodeMessage, + T::Decoded: Verify, + { + verify_value(name, value.ok_or_else(|| ConversionError::missing_field::(name))?) + } +} + +/// Decode a canonical message and verify the domain invariants. +/// +/// The decode step checks the wire representation. The verify step checks the domain invariants. +/// Both steps add `name` to the error path. +/// +/// Use this where the field is not a required optional field of a parent message, such as an +/// element of a repeated field. +pub fn verify_value(name: &'static str, value: T) -> Result +where + T: DecodeMessage, + T::Decoded: Verify, +{ + value + .decode_fields() + .context(name)? + .verify() + .map_err(ConversionError::new) + .context(name) +} + +/// Decode an optional canonical message and verify the domain invariants. +/// +/// Returns `None` when the field is absent. Use [`GrpcStructDecoder::verify_field`] for a field +/// that must be present. +pub fn verify_optional( + name: &'static str, + value: Option, +) -> Result, ConversionError> +where + T: DecodeMessage, + T::Decoded: Verify, +{ + value.map(|value| verify_value(name, value)).transpose() } /// Extension trait on [`prost::Message`] types to create a [`GrpcStructDecoder`] with the parent @@ -68,6 +124,10 @@ impl GrpcDecodeExt for T {} /// Decodes a required optional field from a protobuf message using the message's decoder. /// +/// Use this for node-owned messages and for atomic canonical messages that decode straight to +/// their domain type. Use [`verify!`] for canonical messages that have a separate verification +/// step. +/// /// Uses `stringify!` to automatically derive the field name for error reporting, avoiding /// the duplication between a string literal and the field access. /// @@ -101,6 +161,23 @@ macro_rules! decode { }; } +/// Decodes and verifies a required optional field from a canonical protobuf message. +/// +/// Takes the same two forms as [`decode!`] and reports errors the same way. +/// +/// Only accepts fields whose decoded form implements [`miden_objects::Verify`]. A field that +/// needs external context, or that only supports unchecked construction, must be decoded at the +/// call site so that the chosen capability stays visible. +#[macro_export] +macro_rules! verify { + ($decoder:ident, $msg:ident . $field:ident) => { + $decoder.verify_field(stringify!($field), $msg.$field) + }; + ($decoder:ident, $field:ident) => { + $decoder.verify_field(stringify!($field), $field) + }; +} + #[cfg(test)] mod tests { use miden_protocol::Word; diff --git a/crates/proto/src/decode/utils.rs b/crates/proto/src/decode/utils.rs index 666d7cba3a..f222719e7f 100644 --- a/crates/proto/src/decode/utils.rs +++ b/crates/proto/src/decode/utils.rs @@ -1,9 +1,9 @@ use miden_protocol::Word; use miden_protocol::account::AccountId; -use crate::decode::{ConversionResultExt, GrpcStructDecoder}; +use crate::decode::{ConversionResultExt, GrpcStructDecoder, verify_value}; use crate::errors::ConversionError; -use crate::{decode, generated as proto}; +use crate::{generated as proto, verify}; /// Reads a block range from a request, returning a specific error type if the field is missing pub fn read_block_range( @@ -55,7 +55,7 @@ where { account_ids .into_iter() - .map(|account_id| AccountId::try_from(account_id).map_err(ConversionError::from)) + .map(|account_id| verify_value("account_ids", account_id)) .collect::>() .context("account_ids") .map_err(Into::into) @@ -68,5 +68,5 @@ where E: From, { let decoder = GrpcStructDecoder::::default(); - decode!(decoder, account_id).map_err(|e: ConversionError| e.into()) + verify!(decoder, account_id).map_err(|e: ConversionError| e.into()) } diff --git a/crates/proto/src/domain/account.rs b/crates/proto/src/domain/account.rs index 2a7a64ce61..2fe507e484 100644 --- a/crates/proto/src/domain/account.rs +++ b/crates/proto/src/domain/account.rs @@ -22,10 +22,10 @@ use miden_protocol::crypto::merkle::MerkleError; use miden_protocol::crypto::merkle::smt::{PartialSmt, SmtProof}; use super::try_convert; -use crate::decode; -use crate::decode::{ConversionResultExt, GrpcDecodeExt}; +use crate::decode::{ConversionResultExt, GrpcDecodeExt, verify_optional, verify_value}; use crate::errors::ConversionError; use crate::generated::{self as proto}; +use crate::{decode, verify}; #[cfg(test)] mod tests; @@ -76,8 +76,8 @@ impl TryFrom for AccountRequest { let decoder = value.decoder(); let proto::rpc::AccountRequest { account_id, block_num, details } = value; - let account_id = decode!(decoder, account_id)?; - let block_num = block_num.map(Into::into); + let account_id = verify!(decoder, account_id)?; + let block_num = verify_optional("block_num", block_num)?; let details = details.map(TryFrom::try_from).transpose().context("details")?; @@ -268,7 +268,7 @@ impl TryFrom for AccountVaultDetails { } else { let parsed_assets = assets .into_iter() - .map(|asset| Asset::try_from(asset).map_err(ConversionError::from)) + .map(|asset| verify_value("assets", asset)) .collect::, _>>()?; Ok(Self::Assets(parsed_assets)) } @@ -495,7 +495,7 @@ impl TryFrom )); } let partial_smt: PartialSmt = - decode!(decoder, partial_smt).context("partial_smt")?; + verify!(decoder, partial_smt).context("partial_smt")?; for map_key in &map_keys { partial_smt.get_value(&map_key.hash().as_word()).context("map_keys")?; } @@ -579,7 +579,7 @@ impl TryFrom for AccountStorageDetails { let decoder = value.decoder(); let proto::rpc::AccountStorageDetails { header, map_details } = value; - let header: AccountStorageHeader = decode!(decoder, header)?; + let header: AccountStorageHeader = verify!(decoder, header)?; let map_details: Vec = try_convert(map_details).collect::, _>>().context("map_details")?; @@ -641,9 +641,9 @@ impl TryFrom for AccountResponse { let decoder = value.decoder(); let proto::rpc::AccountResponse { block_num, witness, details } = value; - let block_num = decode!(decoder, block_num)?; + let block_num = verify!(decoder, block_num)?; - let witness = decode!(decoder, witness)?; + let witness = verify!(decoder, witness)?; let details = details.map(TryFrom::try_from).transpose().context("details")?; @@ -704,13 +704,12 @@ impl TryFrom for AccountDetails { storage_details, } = value; - let account_header = decode!(decoder, header)?; + let account_header = verify!(decoder, header)?; let storage_details = decode!(decoder, storage_details)?; let vault_details = decode!(decoder, vault_details)?; - let account_code = - code.map(AccountCode::try_from).transpose().map_err(ConversionError::from)?; + let account_code = verify_optional("code", code)?; Ok(AccountDetails { account_header, diff --git a/crates/proto/src/domain/block_proposal.rs b/crates/proto/src/domain/block_proposal.rs index 2eef60cf34..ac3dd1bd9f 100644 --- a/crates/proto/src/domain/block_proposal.rs +++ b/crates/proto/src/domain/block_proposal.rs @@ -1,8 +1,10 @@ //! Shared decoding of block proposal fields. +use miden_objects::{BuildUnchecked, DecodeMessage}; use miden_protocol::batch::{OrderedBatches, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockInputs, ProposedBlock}; +use crate::decode::{verify_optional, verify_value}; use crate::errors::ConversionError; use crate::generated as proto; @@ -24,15 +26,16 @@ pub(super) fn decode( .into_iter() .enumerate() .map(|(index, batch)| { - miden_objects::conversion::decode_standalone_proven_batch(batch) + batch + .decode_fields() + .and_then(|batch| { + batch.build_unchecked().map_err(miden_objects::ConversionError::new) + }) .map_err(|error| ConversionError::from(error.context(format!("batches[{index}]")))) }) .collect::, _>>()?; - let next_validator_config = next_validator_config.try_into().map_err(ConversionError::from)?; - let next_protocol_config = next_protocol_config - .map(TryInto::try_into) - .transpose() - .map_err(ConversionError::from)?; + let next_validator_config = verify_value("next_validator_config", next_validator_config)?; + let next_protocol_config = verify_optional("next_protocol_config", next_protocol_config)?; let proposed_block = ProposedBlock::new_at(block_inputs.clone(), batches.clone(), timestamp) .map_err(ConversionError::new)? .with_next_validator_config(next_validator_config) diff --git a/crates/proto/src/domain/encryption.rs b/crates/proto/src/domain/encryption.rs index 87728264fe..5c42d05959 100644 --- a/crates/proto/src/domain/encryption.rs +++ b/crates/proto/src/domain/encryption.rs @@ -15,6 +15,7 @@ use miden_protocol::crypto::ies::SealingKey; use miden_protocol::transaction::TransactionId; use miden_protocol::utils::serde::{Deserializable, Serializable}; +use crate::decode::verify_value; use crate::generated as proto; /// Domain tag prefixed to the associated data of sealed transaction inputs. @@ -269,7 +270,8 @@ pub fn verify_transaction_encryption_key( let Some(validator_public_key) = attestation.validator_public_key else { continue; }; - let Ok(validator_public_key) = validator_public_key.try_into() else { + let Ok(validator_public_key) = verify_value("validator_public_key", validator_public_key) + else { continue; }; @@ -281,7 +283,8 @@ pub fn verify_transaction_encryption_key( let Some(signature) = attestation.signature else { continue; }; - let Ok(signature): Result = signature.try_into() else { + let Ok(signature): Result = verify_value("signature", signature) + else { continue; }; if signature.verify(commitment, &validator_public_key) { @@ -552,19 +555,18 @@ mod tests { ); let mut malformed_key = signed_encryption_key(&signer, genesis()); - malformed_key.attestations[0] - .validator_public_key - .as_mut() - .unwrap() - .encoded - .clear(); + malformed_key.attestations[0].validator_public_key = Some(proto::primitives::PublicKey { + key: Some(proto::primitives::public_key::Key::EcdsaK256Keccak(Vec::new())), + }); assert_matches!( verify_transaction_encryption_key(malformed_key, trusted), Err(TransactionEncryptionKeyError::NoTrustedAttestation) ); let mut malformed_signature = signed_encryption_key(&signer, genesis()); - malformed_signature.attestations[0].signature.as_mut().unwrap().encoded.clear(); + malformed_signature.attestations[0].signature = Some(proto::primitives::Signature { + signature: Some(proto::primitives::signature::Signature::EcdsaK256Keccak(Vec::new())), + }); assert_matches!( verify_transaction_encryption_key(malformed_signature, trusted), Err(TransactionEncryptionKeyError::InvalidAttestation) diff --git a/crates/proto/src/domain/proof_request.rs b/crates/proto/src/domain/proof_request.rs index 438857e168..135dc3c1bb 100644 --- a/crates/proto/src/domain/proof_request.rs +++ b/crates/proto/src/domain/proof_request.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; +use miden_objects::{BuildUnchecked, DecodeMessage, Verify}; use miden_protocol::account::AccountId; use miden_protocol::batch::OrderedBatches; use miden_protocol::block::account_tree::AccountWitness; @@ -15,6 +16,7 @@ use miden_protocol::utils::serde::{ Serializable, }; +use crate::decode::{ConversionResultExt, verify_value}; use crate::errors::ConversionError; use crate::generated as proto; @@ -107,25 +109,29 @@ impl TryFrom for BlockInputs { type Error = ConversionError; fn try_from(value: proto::block_proving::BlockInputs) -> Result { - let prev_block_header = required::( - value.prev_block_header, - "prev_block_header", - )?; - let partial_blockchain = required::( - value.partial_blockchain, - "partial_blockchain", - )?; + let prev_block_header: BlockHeader = + build_unchecked_required::( + value.prev_block_header, + "prev_block_header", + )?; + let partial_blockchain: PartialBlockchain = + build_unchecked_required::( + value.partial_blockchain, + "partial_blockchain", + )?; let mut account_witnesses = BTreeMap::::new(); for (index, record) in value.account_witnesses.into_iter().enumerate() { - let account_id = required::( - record.account_id, - "account_id", - )?; - let witness = required::( - record.witness, - "witness", - )?; + let account_id: AccountId = required_verified::< + proto::block_proving::AccountWitnessRecord, + _, + _, + >(record.account_id, "account_id")?; + let witness: AccountWitness = required_verified::< + proto::block_proving::AccountWitnessRecord, + _, + _, + >(record.witness, "witness")?; if account_witnesses.insert(account_id, witness).is_some() { return Err(ConversionError::message(format!( "account_witnesses[{index}]: duplicate requested account ID {account_id}" @@ -141,11 +147,11 @@ impl TryFrom for BlockInputs { miden_protocol::Word, >(record.nullifier, "nullifier")?; let nullifier = Nullifier::from_raw(nullifier_word); - let proof = required::< - proto::block_proving::NullifierWitness, - _, - miden_protocol::crypto::merkle::smt::SmtProof, - >(record.opening, "opening")?; + let proof: miden_protocol::crypto::merkle::smt::SmtProof = + required_verified::( + record.opening, + "opening", + )?; if nullifier_witnesses.insert(nullifier, NullifierWitness::new(proof)).is_some() { return Err(ConversionError::message(format!( "nullifier_witnesses[{index}]: duplicate nullifier {nullifier}" @@ -155,8 +161,8 @@ impl TryFrom for BlockInputs { let mut unauthenticated_note_proofs = BTreeMap::::new(); for (index, proof) in value.unauthenticated_note_proofs.into_iter().enumerate() { - let (note_id, proof) = - <(NoteId, NoteInclusionProof)>::try_from(&proof).map_err(ConversionError::from)?; + let (note_id, proof): (NoteId, NoteInclusionProof) = + verify_value("unauthenticated_note_proofs", proof)?; if unauthenticated_note_proofs.insert(note_id, proof).is_some() { return Err(ConversionError::message(format!( "unauthenticated_note_proofs[{index}]: duplicate note ID {note_id}" @@ -187,6 +193,39 @@ where .map_err(ConversionError::from) } +/// Reads a required canonical field and verifies its domain invariants. +fn required_verified(value: Option, field: &'static str) -> Result +where + M: prost::Message, + T: DecodeMessage, + T::Decoded: Verify, +{ + verify_value(field, value.ok_or_else(|| ConversionError::missing_field::(field))?) +} + +/// Reads a required canonical field and builds it without the checks the domain type cannot make +/// from the message alone. +/// +/// The caller is responsible for the skipped checks. Each decoded type documents which ones it +/// leaves out. +fn build_unchecked_required( + value: Option, + field: &'static str, +) -> Result +where + M: prost::Message, + T: DecodeMessage, + T::Decoded: BuildUnchecked, +{ + value + .ok_or_else(|| ConversionError::missing_field::(field))? + .decode_fields() + .context(field)? + .build_unchecked() + .map_err(ConversionError::new) + .context(field) +} + impl Serializable for BlockProofRequest { fn write_into(&self, target: &mut W) { let Self { tx_batches, block_header, block_inputs } = self; diff --git a/crates/proto/src/domain/protocol_config.rs b/crates/proto/src/domain/protocol_config.rs index d60f0686a5..2c33436887 100644 --- a/crates/proto/src/domain/protocol_config.rs +++ b/crates/proto/src/domain/protocol_config.rs @@ -3,6 +3,7 @@ use miden_protocol::block::BlockHeader; use miden_protocol::protocol_config::ProtocolConfig; +use crate::decode::verify_value; use crate::errors::ConversionError; use crate::generated::protocol_config::ProtocolConfig as ProtoProtocolConfig; @@ -12,10 +13,10 @@ pub fn ensure_protocol_config_is_present_and_matches_header( config: Option, header: &BlockHeader, ) -> Result { - let config: ProtocolConfig = config - .ok_or_else(|| ConversionError::message("protocol config is missing"))? - .try_into() - .map_err(ConversionError::from)?; + let config: ProtocolConfig = verify_value( + "protocol_config", + config.ok_or_else(|| ConversionError::message("protocol config is missing"))?, + )?; let calculated = config.to_commitment(); let expected = header.protocol_config_commitment(); if calculated != expected { diff --git a/crates/proto/src/domain/submission.rs b/crates/proto/src/domain/submission.rs index 81d192be62..c8dccbce8e 100644 --- a/crates/proto/src/domain/submission.rs +++ b/crates/proto/src/domain/submission.rs @@ -1,8 +1,10 @@ +use miden_objects::{BuildUnchecked, DecodeMessage, VerifyWith}; use miden_protocol::MIN_PROOF_SECURITY_LEVEL; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::transaction::ProvenTransaction; +use crate::decode::{ConversionResultExt, verify_value}; use crate::errors::ConversionError; use crate::generated as proto; @@ -18,15 +20,18 @@ impl TryFrom for ProvenTransacti fn try_from( value: proto::submission::ProvenTransactionSubmission, ) -> Result { - let transaction = value + let transaction: ProvenTransaction = value .transaction .ok_or_else(|| { ConversionError::missing_field::( "transaction", ) })? - .try_into() - .map_err(ConversionError::from)?; + .decode_fields() + .context("transaction")? + .build_unchecked() + .map_err(ConversionError::new) + .context("transaction")?; let sealed_transaction_inputs = value.sealed_transaction_inputs.ok_or_else(|| { ConversionError::missing_field::( "sealed_transaction_inputs", @@ -72,30 +77,38 @@ impl TryFrom for TransactionBatchSubmission "reference_block_header", ) })? - .try_into() - .map_err(ConversionError::from)?; - let batch_reference_num: BlockNumber = batch_message - .reference_block_num - .ok_or_else(|| { + .decode_fields() + .context("reference_block_header")? + .build_unchecked() + .map_err(ConversionError::new) + .context("reference_block_header")?; + let batch_reference_num: BlockNumber = verify_value( + "reference_block_num", + batch_message.reference_block_num.ok_or_else(|| { ConversionError::missing_field::( "reference_block_num", ) - })? - .into(); + })?, + )?; if batch_reference_num != proposed_reference_header.block_num() { return Err(ConversionError::message( "batch reference block number does not match proposal", )); } - let proposed_batch = miden_objects::conversion::decode_proposed_batch( - proposed_message, - MIN_PROOF_SECURITY_LEVEL, - ) - .map_err(ConversionError::from)?; + let proposed_batch = proposed_message + .decode_fields() + .context("proposed_batch")? + .verify_with(MIN_PROOF_SECURITY_LEVEL) + .map_err(ConversionError::new) + .context("proposed_batch")?; - let batch = miden_objects::conversion::decode_proven_batch(batch_message, &proposed_batch) - .map_err(ConversionError::from)?; + let batch = batch_message + .decode_fields() + .context("batch")? + .verify_with(&proposed_batch) + .map_err(ConversionError::new) + .context("batch")?; if value.sealed_transaction_inputs.len() != proposed_batch.transactions().len() { return Err(ConversionError::message(format!( diff --git a/crates/proto/src/lib.rs b/crates/proto/src/lib.rs index b5b8d0ff37..b4db10f38e 100644 --- a/crates/proto/src/lib.rs +++ b/crates/proto/src/lib.rs @@ -14,4 +14,5 @@ pub use domain::sign_block_request::SignBlockRequest; pub use domain::submission::{ProvenTransactionSubmission, TransactionBatchSubmission}; pub use domain::{convert, try_convert}; pub use generated::server; +pub use miden_objects::{BuildUnchecked, DecodeMessage, Verify, VerifyWith}; pub use prost; diff --git a/crates/proto/tests/node_conversions.rs b/crates/proto/tests/node_conversions.rs index fb711a77e9..f19817882d 100644 --- a/crates/proto/tests/node_conversions.rs +++ b/crates/proto/tests/node_conversions.rs @@ -7,7 +7,7 @@ use miden_node_proto::domain::submission::{ TransactionBatchSubmission, }; use miden_node_proto::generated; -use miden_objects::proto; +use miden_objects::{DecodeMessage, proto}; use miden_protocol::Word; use miden_protocol::account::{ AccountId, @@ -421,8 +421,7 @@ fn batch_submission_rejects_proof_that_does_not_match_proposal() { #[test] fn canonical_conversion_errors_map_to_invalid_argument() { - let error = miden_protocol::account::AccountId::try_from(proto::account::AccountId::default()) - .unwrap_err(); + let error = proto::account::AccountId::default().decode_fields().unwrap_err(); let status: tonic::Status = miden_node_proto::errors::ConversionError::from(error).into(); assert_eq!(status.code(), tonic::Code::InvalidArgument); diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 5de57036f4..bae78a9f2f 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -9,6 +9,7 @@ use miden_node_proto::domain::block::InvalidBlockRange; use miden_node_proto::generated::rpc::MempoolStats as ProtoMempoolStats; use miden_node_proto::generated::rpc::api_server::Api; use miden_node_proto::generated::{self as proto}; +use miden_node_proto::{BuildUnchecked, DecodeMessage}; use miden_node_store::state::State; use miden_node_store::{DatabaseError, GetBlockHeaderError}; use miden_node_tracing::{miden_instrument, warn}; @@ -185,7 +186,11 @@ impl RpcService { .await?; let header = header.into_inner().block_header.context("response is missing the header")?; - BlockHeader::try_from(header).context("failed to parse response") + header + .decode_fields() + .context("failed to parse response")? + .build_unchecked() + .context("failed to build response header") } /// Returns the given block's onchain header. diff --git a/crates/rpc/src/server/api/get_notes_by_id.rs b/crates/rpc/src/server/api/get_notes_by_id.rs index 937bdf75a8..d37cf7a3a9 100644 --- a/crates/rpc/src/server/api/get_notes_by_id.rs +++ b/crates/rpc/src/server/api/get_notes_by_id.rs @@ -1,9 +1,8 @@ -use miden_node_proto::generated as proto; use miden_node_proto::generated::rpc::CommittedNote; +use miden_node_proto::{DecodeMessage, Verify, generated as proto}; use miden_node_store::NoteRecord; use miden_node_tracing::{debug, miden_instrument, miden_span_record}; use miden_node_utils::limiter::QueryParamNoteIdLimit; -use miden_protocol::Word; use miden_protocol::note::NoteId; use tonic::Status; @@ -36,13 +35,16 @@ impl proto::server::rpc_api::GetNotesById for RpcService { ) -> tonic::Result { check::(request.note_ids.len())?; - let note_ids: Vec = request + let note_ids: Vec = request .note_ids .into_iter() - .map(Word::try_from) + .map(|note_id| { + note_id.decode_fields().and_then(|note_id| { + note_id.verify().map_err(miden_objects::ConversionError::new) + }) + }) .collect::>() .map_err(|err| Status::invalid_argument(format!("invalid note ID: {err}")))?; - let note_ids: Vec = note_ids.into_iter().map(NoteId::from_raw).collect(); miden_span_record!( note.ids = ¬e_ids[..note_ids.len().min(10)], note.count = note_ids.len() @@ -79,7 +81,7 @@ fn note_record_to_proto(note: NoteRecord) -> proto::rpc::CommittedNote { inclusion_path: Some(note.inclusion_path.into()), }); let note = Some(proto::note::Note { - metadata: Some(note.metadata.into()), + metadata: Some(note.metadata.into_partial_metadata().into()), note_details: note.details.map(Into::into), note_attachments: Some(note.attachments.into()), }); diff --git a/crates/rpc/src/server/api/register_account.rs b/crates/rpc/src/server/api/register_account.rs index b81ee4689a..2fd3dcec80 100644 --- a/crates/rpc/src/server/api/register_account.rs +++ b/crates/rpc/src/server/api/register_account.rs @@ -1,4 +1,4 @@ -use miden_node_proto::generated as proto; +use miden_node_proto::{DecodeMessage, Verify, generated as proto}; use miden_node_store::allowlist::{AllowlistError, InvitationCode}; use miden_node_tracing::{ErrorReport, miden_instrument, miden_span_record}; use miden_protocol::account::AccountId; @@ -29,9 +29,10 @@ impl proto::server::rpc_api::RegisterAccount for RpcService { ) -> tonic::Result { let account_id: AccountId = request .account_id - .clone() .ok_or_else(|| Status::invalid_argument("missing account_id"))? - .try_into() + .decode_fields() + .map_err(|_| Status::invalid_argument("invalid account_id"))? + .verify() .map_err(|_| Status::invalid_argument("invalid account_id"))?; miden_span_record!(account.id = account_id); diff --git a/crates/rpc/src/server/api/submit_auth_tx_batch.rs b/crates/rpc/src/server/api/submit_auth_tx_batch.rs index 7ae04f9eb0..a3ab5184e3 100644 --- a/crates/rpc/src/server/api/submit_auth_tx_batch.rs +++ b/crates/rpc/src/server/api/submit_auth_tx_batch.rs @@ -1,6 +1,6 @@ use miden_node_block_producer::store::TransactionInputs; -use miden_node_proto::generated as proto; use miden_node_proto::generated::server::sequencer_api; +use miden_node_proto::{DecodeMessage, VerifyWith, generated as proto}; use miden_node_tracing::ErrorReport; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_protocol::batch::ProposedBatch; @@ -54,11 +54,11 @@ fn decode_authenticated_transaction_batch( let proposed_batch = request .proposed_batch .ok_or_else(|| Status::invalid_argument("missing `proposed_batch` field"))?; - let batch = miden_objects::conversion::decode_proposed_batch( - proposed_batch, - miden_protocol::MIN_PROOF_SECURITY_LEVEL, - ) - .map_err(|err| Status::invalid_argument(format!("invalid proposed_batch: {err}")))?; + let batch = proposed_batch + .decode_fields() + .map_err(|err| Status::invalid_argument(format!("invalid proposed_batch: {err}")))? + .verify_with(miden_protocol::MIN_PROOF_SECURITY_LEVEL) + .map_err(|err| Status::invalid_argument(format!("invalid proposed_batch: {err}")))?; if batch.transactions().len() != request.auth_inputs.len() { return Err(Status::invalid_argument(format!( diff --git a/crates/rpc/src/server/api/submit_proven_tx.rs b/crates/rpc/src/server/api/submit_proven_tx.rs index 3ddb1e635e..a5b5eb088b 100644 --- a/crates/rpc/src/server/api/submit_proven_tx.rs +++ b/crates/rpc/src/server/api/submit_proven_tx.rs @@ -1,7 +1,7 @@ use miden_node_block_producer::store::get_tx_inputs; use miden_node_block_producer::{AuthenticatedTransaction, ensure_transaction_has_fee}; use miden_node_proto::clients::{SequencerClient, ValidatorClient}; -use miden_node_proto::generated as proto; +use miden_node_proto::{BuildUnchecked, DecodeMessage, generated as proto}; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_node_tracing::{ErrorReport, debug, miden_instrument, miden_span_record, trace}; use miden_protocol::MIN_PROOF_SECURITY_LEVEL; @@ -53,7 +53,9 @@ impl proto::server::rpc_api::SubmitProvenTx for RpcService { .transaction .take() .ok_or_else(|| Status::invalid_argument("missing `transaction` field"))? - .try_into() + .decode_fields() + .map_err(|err| Status::invalid_argument(format!("invalid transaction: {err}")))? + .build_unchecked() .map_err(|err| Status::invalid_argument(format!("invalid transaction: {err}")))?; miden_span_record!( diff --git a/crates/rpc/src/server/api/submit_proven_tx_batch.rs b/crates/rpc/src/server/api/submit_proven_tx_batch.rs index 92ccabf1c4..9f2624c1d8 100644 --- a/crates/rpc/src/server/api/submit_proven_tx_batch.rs +++ b/crates/rpc/src/server/api/submit_proven_tx_batch.rs @@ -3,7 +3,7 @@ use miden_node_proto::clients::{SequencerClient, ValidatorClient}; use miden_node_proto::generated as proto; use miden_node_tracing::spawn::spawn_blocking_in_current_span; use miden_node_tracing::{ErrorReport, debug, miden_instrument, miden_span_record, trace}; -use miden_objects::conversion::{decode_proposed_batch, decode_proven_batch}; +use miden_objects::{DecodeMessage, VerifyWith}; use miden_protocol::MIN_PROOF_SECURITY_LEVEL; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_tx_batch::BatchVerifier; @@ -55,7 +55,11 @@ impl proto::server::rpc_api::SubmitProvenTxBatch for RpcService { }) .ok_or_else(|| Status::invalid_argument("missing `proposed_batch` field"))?; let proposed_batch = spawn_blocking_in_current_span(move || { - decode_proposed_batch(proposed_batch_message, MIN_PROOF_SECURITY_LEVEL) + proposed_batch_message.decode_fields().and_then(|batch| { + batch + .verify_with(MIN_PROOF_SECURITY_LEVEL) + .map_err(miden_objects::ConversionError::new) + }) }) .await .map_err(|err| Status::internal(format!("proposed batch decoding task failed: {err}")))? @@ -67,7 +71,10 @@ impl proto::server::rpc_api::SubmitProvenTxBatch for RpcService { request.batch.take() }) .ok_or_else(|| Status::invalid_argument("missing `batch` field"))?; - let proven_batch = decode_proven_batch(proven_batch_message, &proposed_batch) + let proven_batch = proven_batch_message + .decode_fields() + .map_err(|err| Status::invalid_argument(format!("invalid proven_batch: {err}")))? + .verify_with(&proposed_batch) .map_err(|err| Status::invalid_argument(format!("invalid proven_batch: {err}")))?; miden_span_record!( diff --git a/crates/rpc/src/server/api/sync_account_storage_maps.rs b/crates/rpc/src/server/api/sync_account_storage_maps.rs index 645c69f5e6..b29572b48a 100644 --- a/crates/rpc/src/server/api/sync_account_storage_maps.rs +++ b/crates/rpc/src/server/api/sync_account_storage_maps.rs @@ -36,7 +36,7 @@ impl proto::server::rpc_api::SyncAccountStorageMaps for RpcService { _extensions: &tonic::codegen::http::Extensions, ) -> tonic::Result { let account_id = read_account_id::( - request.account_id.clone(), + request.account_id, )?; let range = read_block_range::(request.block_range, "SyncAccountStorageMapsRequest")?; diff --git a/crates/rpc/src/server/api/sync_account_vault.rs b/crates/rpc/src/server/api/sync_account_vault.rs index 73ff5862b8..6991aa5c38 100644 --- a/crates/rpc/src/server/api/sync_account_vault.rs +++ b/crates/rpc/src/server/api/sync_account_vault.rs @@ -36,9 +36,8 @@ impl proto::server::rpc_api::SyncAccountVault for RpcService { _metadata: &tonic::metadata::MetadataMap, _extensions: &tonic::codegen::http::Extensions, ) -> tonic::Result { - let account_id = read_account_id::( - request.account_id.clone(), - )?; + let account_id = + read_account_id::(request.account_id)?; let range = read_block_range::(request.block_range, "SyncAccountVaultRequest")?; miden_span_record!( diff --git a/crates/rpc/src/server/api/sync_notes.rs b/crates/rpc/src/server/api/sync_notes.rs index 2434106aa2..ffc7b4e47d 100644 --- a/crates/rpc/src/server/api/sync_notes.rs +++ b/crates/rpc/src/server/api/sync_notes.rs @@ -1,5 +1,7 @@ use miden_node_proto::decode::read_block_range; use miden_node_proto::generated as proto; +#[cfg(test)] +use miden_node_proto::{DecodeMessage, Verify}; use miden_node_store::{NoteSyncError, NoteSyncRecord}; use miden_node_tracing::{debug, miden_instrument, miden_span_record}; use miden_node_utils::limiter::QueryParamNoteTagLimit; @@ -255,7 +257,9 @@ mod tests { attachment_schemes, attachments_commitment: Some(attachments_commitment.into()), } - .try_into() + .decode_fields() + .unwrap() + .verify() .unwrap(); assert_eq!(reconstructed.to_commitment(), expected_metadata_commitment); } @@ -362,8 +366,7 @@ mod tests { validator_config: Some(proto::blockchain::ValidatorConfig { keys: vec![ proto::primitives::PublicKey { - variant: proto::primitives::PublicKeyVariant::EcdsaK256Keccak as i32, - encoded: vec![2; 33], + key: Some(proto::primitives::public_key::Key::EcdsaK256Keccak(vec![2; 33])), }; ValidatorConfig::MAX_VALIDATORS ], diff --git a/crates/rpc/src/server/api/sync_transactions.rs b/crates/rpc/src/server/api/sync_transactions.rs index 92367f6e6a..aac1282665 100644 --- a/crates/rpc/src/server/api/sync_transactions.rs +++ b/crates/rpc/src/server/api/sync_transactions.rs @@ -41,7 +41,7 @@ impl proto::server::rpc_api::SyncTransactions for RpcService { let range = read_block_range::(request.block_range, "SyncTransactionsRequest")?; let n_accounts = request.account_ids.len(); let account_ids = - read_account_ids::(request.account_ids.iter().take(10).cloned())?; + read_account_ids::(request.account_ids.iter().take(10).copied())?; miden_span_record!( block_range.from = range.block_from, diff --git a/crates/rpc/src/tests.rs b/crates/rpc/src/tests.rs index 78ab86d4ea..ffe80218a9 100644 --- a/crates/rpc/src/tests.rs +++ b/crates/rpc/src/tests.rs @@ -26,6 +26,7 @@ use miden_node_proto::generated::rpc::api_server::Api; use miden_node_proto::generated::sequencer::api_server::Api as SequencerApi; use miden_node_proto::generated::{self as proto}; use miden_node_proto::server::{ntx_builder_api, rpc_api, sequencer_api, validator_api}; +use miden_node_proto::{BuildUnchecked, DecodeMessage, Verify}; use miden_node_store::DataDirectory; use miden_node_store::allowlist::{AccountAllowlist, InvitationCode, InvitationEntry}; use miden_node_store::genesis::GenesisBlock; @@ -754,8 +755,14 @@ async fn rpc_server_forwards_valid_deferred_proofs_and_rejects_missing_witnesses { let submissions = submissions.lock().unwrap(); assert_eq!(submissions.len(), 1); - let forwarded: ProvenTransaction = - submissions[0].transaction.clone().unwrap().try_into().unwrap(); + let forwarded: ProvenTransaction = submissions[0] + .transaction + .clone() + .unwrap() + .decode_fields() + .unwrap() + .build_unchecked() + .unwrap(); assert_eq!(forwarded.id(), fixture.transaction.id()); assert_eq!(forwarded.proof(), fixture.transaction.proof()); } @@ -1235,12 +1242,13 @@ fn test_encryption_key() -> proto::submission::TransactionEncryptionKey { public_key: vec![7; 32], attestations: vec![proto::submission::ValidatorKeyAttestation { validator_public_key: Some(proto::primitives::PublicKey { - variant: proto::primitives::PublicKeyVariant::EcdsaK256Keccak as i32, - encoded: vec![8; 33], + key: Some(proto::primitives::public_key::Key::EcdsaK256Keccak(vec![8; 33])), }), signature: Some(proto::primitives::Signature { - variant: proto::primitives::SignatureVariant::EcdsaK256Keccak as i32, - encoded: vec![9; 65], + signature: Some(proto::primitives::signature::Signature::EcdsaK256Keccak(vec![ + 9; + 65 + ])), }), }], next_key: Some(proto::submission::NextTransactionEncryptionKey { @@ -1657,7 +1665,7 @@ async fn register_account_validates_input_and_preserves_registrations() { }, proto::rpc::RegisterAccountRequest { account_id: None, ..request.clone() }, proto::rpc::RegisterAccountRequest { - account_id: Some(proto::account::AccountId { id: vec![0] }), + account_id: Some(proto::account::AccountId::default()), ..request.clone() }, ] { @@ -1917,9 +1925,20 @@ async fn sync_chain_mmr_returns_delta() { let mmr_delta = response.mmr_delta.expect("mmr_delta should exist"); assert_eq!(mmr_delta.forest, 0); assert!(mmr_delta.update_data.is_empty()); - let config: ProtocolConfig = - response.protocol_config.expect("genesis config").try_into().unwrap(); - let header: BlockHeader = response.block_header.unwrap().try_into().unwrap(); + let config: ProtocolConfig = response + .protocol_config + .expect("genesis config") + .decode_fields() + .unwrap() + .verify() + .unwrap(); + let header: BlockHeader = response + .block_header + .unwrap() + .decode_fields() + .unwrap() + .build_unchecked() + .unwrap(); assert_eq!(config.to_commitment(), header.protocol_config_commitment()); } @@ -1940,8 +1959,14 @@ async fn header_protocol_config_is_opt_in() { assert_eq!(response.protocol_config.is_some(), include == Some(true)); assert!(response.mmr_path.is_some()); if let Some(config) = response.protocol_config { - let config: ProtocolConfig = config.try_into().unwrap(); - let header: BlockHeader = response.block_header.unwrap().try_into().unwrap(); + let config: ProtocolConfig = config.decode_fields().unwrap().verify().unwrap(); + let header: BlockHeader = response + .block_header + .unwrap() + .decode_fields() + .unwrap() + .build_unchecked() + .unwrap(); assert_eq!(config.to_commitment(), header.protocol_config_commitment()); } } @@ -2026,8 +2051,15 @@ async fn block_subscription_starts_with_matching_config() { .unwrap() .into_inner(); let event = stream.message().await.unwrap().unwrap(); - let block: SignedBlock = event.block.unwrap().try_into().unwrap(); - let config: ProtocolConfig = event.protocol_config.expect("initial config").try_into().unwrap(); + let block: SignedBlock = + event.block.unwrap().decode_fields().unwrap().build_unchecked().unwrap(); + let config: ProtocolConfig = event + .protocol_config + .expect("initial config") + .decode_fields() + .unwrap() + .verify() + .unwrap(); assert_eq!(config.to_commitment(), block.header().protocol_config_commitment()); } @@ -2108,7 +2140,13 @@ async fn protocol_config_transitions_follow_response_headers() { .unwrap() .into_inner(); assert_eq!(response.protocol_config.is_some(), included); - let header: BlockHeader = response.block_header.unwrap().try_into().unwrap(); + let header: BlockHeader = response + .block_header + .unwrap() + .decode_fields() + .unwrap() + .build_unchecked() + .unwrap(); assert_eq!(header.block_num(), 4.into()); if included { assert_eq!( @@ -2130,7 +2168,8 @@ async fn protocol_config_transitions_follow_response_headers() { .await .unwrap() .into_inner(); - let header: BlockHeader = proven.block_header.unwrap().try_into().unwrap(); + let header: BlockHeader = + proven.block_header.unwrap().decode_fields().unwrap().build_unchecked().unwrap(); assert_eq!(header.block_num(), 0.into()); assert_eq!( ensure_protocol_config_is_present_and_matches_header(proven.protocol_config, &header) @@ -2146,7 +2185,8 @@ async fn protocol_config_transitions_follow_response_headers() { .into_inner(); for height in start..=4 { let response = stream.message().await.unwrap().unwrap(); - let block: SignedBlock = response.block.unwrap().try_into().unwrap(); + let block: SignedBlock = + response.block.unwrap().decode_fields().unwrap().build_unchecked().unwrap(); assert_eq!(block.header().block_num(), height.into()); let included = height == start || height == 2 || height == 4; assert_eq!(response.protocol_config.is_some(), included); diff --git a/proto/proto/README.md b/proto/proto/README.md index 8d7be16505..7936030076 100644 --- a/proto/proto/README.md +++ b/proto/proto/README.md @@ -5,7 +5,7 @@ These protobuf files are part of the [Miden node](https://github.com/0xMiden/nod The root directory contains the public RPC and remote prover protocols. The `types` directory contains node-owned messages for service workflows. The `internal` directory contains the internal component protocols. -Canonical protocol objects come from `miden-objects` `0.17.0-rc.3`. Do not copy these object schemas into this +Canonical protocol objects come from `miden-objects` `0.17.0-rc.4`. Do not copy these object schemas into this directory. The build resolves canonical imports through `miden_objects::FILE_DESCRIPTOR_SET`. It includes imported schemas in each exported service descriptor set. The raw files in this directory alone are not sufficient to generate bindings.