diff --git a/Cargo.lock b/Cargo.lock index 5346f51d3..1014985bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -216,7 +216,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -386,7 +386,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -397,7 +397,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -505,7 +505,7 @@ dependencies = [ "bitflags 2.5.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "proc-macro2", @@ -513,7 +513,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -694,7 +694,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -711,9 +711,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comfy-table" -version = "7.1.1" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" dependencies = [ "crossterm", "strum", @@ -1073,9 +1073,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.13" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -1096,9 +1096,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.26" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -1162,6 +1162,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -1303,6 +1312,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1387,12 +1405,13 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.1.3" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.5.0", "libc", + "redox_syscall", ] [[package]] @@ -1452,16 +1471,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" dependencies = [ - "logos-derive 0.13.0", -] - -[[package]] -name = "logos" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" -dependencies = [ - "logos-derive 0.14.0", + "logos-derive", ] [[package]] @@ -1475,22 +1485,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.58", -] - -[[package]] -name = "logos-codegen" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" -dependencies = [ - "beef", - "fnv", - "lazy_static", - "proc-macro2", - "quote", - "regex-syntax 0.8.3", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -1499,16 +1494,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" dependencies = [ - "logos-codegen 0.13.0", -] - -[[package]] -name = "logos-derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" -dependencies = [ - "logos-codegen 0.14.0", + "logos-codegen", ] [[package]] @@ -1672,7 +1658,7 @@ dependencies = [ [[package]] name = "miden-lib" version = "0.2.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=bobbon-block-note-tree#e7f22c9a348b2a878ec52efd89575a2c989bd75b" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#1d32f951e29c2e12526520f734aaba02555e8f79" dependencies = [ "miden-assembly 0.9.1", "miden-objects 0.2.0", @@ -1707,7 +1693,7 @@ dependencies = [ "async-trait", "clap", "figment", - "itertools", + "itertools 0.12.1", "miden-air 0.9.1", "miden-node-proto 0.2.0", "miden-node-store", @@ -1840,7 +1826,7 @@ name = "miden-node-test-macro" version = "0.1.0" dependencies = [ "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -1851,7 +1837,7 @@ checksum = "7dd92792c7a90a2ea6e7e7e2f1c84d675b9ba84385cbf95ce04ab1f04cf46a1f" dependencies = [ "anyhow", "figment", - "itertools", + "itertools 0.12.1", "miden-objects 0.1.1", "serde", "tracing", @@ -1864,7 +1850,7 @@ version = "0.2.0" dependencies = [ "anyhow", "figment", - "itertools", + "itertools 0.12.1", "miden-objects 0.2.0", "serde", "tracing", @@ -1890,7 +1876,7 @@ dependencies = [ [[package]] name = "miden-objects" version = "0.2.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=bobbon-block-note-tree#e7f22c9a348b2a878ec52efd89575a2c989bd75b" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#1d32f951e29c2e12526520f734aaba02555e8f79" dependencies = [ "miden-assembly 0.9.1", "miden-core 0.9.1", @@ -1982,7 +1968,7 @@ dependencies = [ [[package]] name = "miden-tx" version = "0.2.0" -source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=bobbon-block-note-tree#e7f22c9a348b2a878ec52efd89575a2c989bd75b" +source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#1d32f951e29c2e12526520f734aaba02555e8f79" dependencies = [ "miden-lib 0.2.0", "miden-objects 0.2.0", @@ -2043,7 +2029,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2091,9 +2077,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.10.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nom" @@ -2225,7 +2211,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2310,7 +2296,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2346,14 +2332,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2386,7 +2372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2415,7 +2401,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", "version_check", "yansi", ] @@ -2442,9 +2428,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", "prost-derive", @@ -2452,13 +2438,13 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" +checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" dependencies = [ "bytes", - "heck 0.5.0", - "itertools", + "heck 0.4.1", + "itertools 0.11.0", "log", "multimap", "once_cell", @@ -2467,30 +2453,31 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.58", + "syn 2.0.55", "tempfile", + "which", ] [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" dependencies = [ "anyhow", - "itertools", + "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] name = "prost-reflect" -version = "0.13.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f" +checksum = "9ae9372e3227f3685376a0836e5c248611eafc95a0be900d44bc6cdf225b700f" dependencies = [ - "logos 0.14.0", + "logos", "miette", "once_cell", "prost", @@ -2499,9 +2486,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ "prost", ] @@ -2527,7 +2514,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "033b939d76d358f7c32120c86c71f515bae45e64f2bde455200356557276276c" dependencies = [ - "logos 0.13.0", + "logos", "miette", "prost-types", "thiserror", @@ -2618,9 +2605,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", @@ -2782,7 +2769,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2895,27 +2882,27 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" -version = "0.26.2" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -2952,9 +2939,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -3017,7 +3004,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -3113,7 +3100,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -3223,7 +3210,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -3278,7 +3265,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] @@ -3501,7 +3488,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", "wasm-bindgen-shared", ] @@ -3523,7 +3510,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3534,6 +3521,18 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3753,9 +3752,9 @@ dependencies = [ [[package]] name = "winter-math" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c36d2a04b4f79f2c8c6945aab6545b7310a0cd6ae47b9210750400df6775a04" +checksum = "a0c91111b368b08c5a76009514e9b6d26af41fbb28604ea77a249282323b64d5" dependencies = [ "serde", "winter-utils", @@ -3840,7 +3839,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.55", ] [[package]] diff --git a/block-producer/src/test_utils/account.rs b/block-producer/src/test_utils/account.rs index 009cbf958..f375427e2 100644 --- a/block-producer/src/test_utils/account.rs +++ b/block-producer/src/test_utils/account.rs @@ -1,4 +1,7 @@ -use miden_objects::{accounts::get_account_seed, Hasher}; +use miden_objects::{ + accounts::{get_account_seed, AccountStorageType, AccountType}, + Hasher, +}; use super::*; @@ -19,8 +22,8 @@ impl MockPrivateAccount { ) -> Self { let account_seed = get_account_seed( init_seed, - miden_objects::accounts::AccountType::RegularAccountUpdatableCode, - miden_objects::accounts::AccountStorageType::OffChain, + AccountType::RegularAccountUpdatableCode, + AccountStorageType::OffChain, Digest::default(), Digest::default(), ) diff --git a/proto/proto/account.proto b/proto/proto/account.proto index 25d63bf73..ec4b5b7eb 100644 --- a/proto/proto/account.proto +++ b/proto/proto/account.proto @@ -10,8 +10,13 @@ message AccountId { fixed64 id = 1; } -message AccountInfo { - AccountId account_id = 1; +message AccountHashUpdate { + account.AccountId account_id = 1; digest.Digest account_hash = 2; - fixed32 block_num = 3; + uint32 block_num = 3; +} + +message AccountInfo { + AccountHashUpdate update = 1; + optional bytes details = 2; } diff --git a/proto/proto/requests.proto b/proto/proto/requests.proto index d872b3d58..bf0082de0 100644 --- a/proto/proto/requests.proto +++ b/proto/proto/requests.proto @@ -84,3 +84,9 @@ message ListNullifiersRequest {} message ListAccountsRequest {} message ListNotesRequest {} + +// Returns the latest state of an account with the specified ID. +message GetAccountDetailsRequest { + // Account ID to get details. + account.AccountId account_id = 1; +} diff --git a/proto/proto/responses.proto b/proto/proto/responses.proto index d7b8f487b..239d7a7a1 100644 --- a/proto/proto/responses.proto +++ b/proto/proto/responses.proto @@ -20,12 +20,6 @@ message GetBlockHeaderByNumberResponse { block_header.BlockHeader block_header = 1; } -message AccountHashUpdate { - account.AccountId account_id = 1; - digest.Digest account_hash = 2; - uint32 block_num = 3; -} - message NullifierUpdate { digest.Digest nullifier = 1; fixed32 block_num = 2; @@ -42,7 +36,7 @@ message SyncStateResponse { mmr.MmrDelta mmr_delta = 3; // a list of account hashes updated after `block_num + 1` but not after `block_header.block_num` - repeated AccountHashUpdate accounts = 5; + repeated account.AccountHashUpdate accounts = 5; // a list of all notes together with the Merkle paths from `block_header.note_root` repeated note.NoteSyncRecord notes = 6; @@ -71,7 +65,7 @@ message GetBlockInputsResponse { // Peaks of the above block's mmr, The `forest` value is equal to the block number. repeated digest.Digest mmr_peaks = 2; - // The hashes of the requested accouts and their authentication paths + // The hashes of the requested accounts and their authentication paths repeated AccountBlockInputRecord account_states = 3; // The requested nullifiers and their authentication paths @@ -113,3 +107,8 @@ message ListNotesResponse { // Lists all notes of the current chain repeated note.Note notes = 1; } + +message GetAccountDetailsResponse { + // Account info (with details for on-chain accounts) + account.AccountInfo account = 1; +} diff --git a/proto/proto/rpc.proto b/proto/proto/rpc.proto index 015b0a9d3..10a61108a 100644 --- a/proto/proto/rpc.proto +++ b/proto/proto/rpc.proto @@ -10,4 +10,5 @@ service Api { rpc GetBlockHeaderByNumber(requests.GetBlockHeaderByNumberRequest) returns (responses.GetBlockHeaderByNumberResponse) {} rpc SyncState(requests.SyncStateRequest) returns (responses.SyncStateResponse) {} rpc SubmitProvenTransaction(requests.SubmitProvenTransactionRequest) returns (responses.SubmitProvenTransactionResponse) {} + rpc GetAccountDetails(requests.GetAccountDetailsRequest) returns (responses.GetAccountDetailsResponse) {} } diff --git a/proto/proto/store.proto b/proto/proto/store.proto index 4c5557755..b307f2bcc 100644 --- a/proto/proto/store.proto +++ b/proto/proto/store.proto @@ -17,4 +17,5 @@ service Api { rpc ListNullifiers(requests.ListNullifiersRequest) returns (responses.ListNullifiersResponse) {} rpc ListAccounts(requests.ListAccountsRequest) returns (responses.ListAccountsResponse) {} rpc ListNotes(requests.ListNotesRequest) returns (responses.ListNotesResponse) {} + rpc GetAccountDetails(requests.GetAccountDetailsRequest) returns (responses.GetAccountDetailsResponse) {} } diff --git a/proto/src/domain/accounts.rs b/proto/src/domain/accounts.rs index 9669fea75..d8a1e383a 100644 --- a/proto/src/domain/accounts.rs +++ b/proto/src/domain/accounts.rs @@ -2,13 +2,20 @@ use std::fmt::{Debug, Display, Formatter}; use miden_node_utils::formatting::format_opt; use miden_objects::{ - accounts::AccountId, crypto::merkle::MerklePath, transaction::AccountDetails, Digest, + accounts::{Account, AccountId}, + crypto::{hash::rpo::RpoDigest, merkle::MerklePath}, + transaction::AccountDetails, + utils::Serializable, + Digest, }; use crate::{ errors::{ConversionError, MissingFieldHelper}, generated::{ - account::AccountId as AccountIdPb, + account::{ + AccountHashUpdate as AccountHashUpdatePb, AccountId as AccountIdPb, + AccountInfo as AccountInfoPb, + }, requests::AccountUpdate, responses::{AccountBlockInputRecord, AccountTransactionInputRecord}, }, @@ -72,6 +79,38 @@ impl TryFrom for AccountId { // ACCOUNT UPDATE // ================================================================================================ +#[derive(Debug, PartialEq)] +pub struct AccountHashUpdate { + pub account_id: AccountId, + pub account_hash: RpoDigest, + pub block_num: u32, +} + +impl From<&AccountHashUpdate> for AccountHashUpdatePb { + fn from(update: &AccountHashUpdate) -> Self { + Self { + account_id: Some(update.account_id.into()), + account_hash: Some(update.account_hash.into()), + block_num: update.block_num, + } + } +} + +#[derive(Debug, PartialEq)] +pub struct AccountInfo { + pub update: AccountHashUpdate, + pub details: Option, +} + +impl From<&AccountInfo> for AccountInfoPb { + fn from(AccountInfo { update, details }: &AccountInfo) -> Self { + Self { + update: Some(update.into()), + details: details.as_ref().map(|account| account.to_bytes()), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct UpdatedAccount { pub account_id: AccountId, diff --git a/proto/src/generated/account.rs b/proto/src/generated/account.rs index 923c1d896..72fd8afbf 100644 --- a/proto/src/generated/account.rs +++ b/proto/src/generated/account.rs @@ -12,11 +12,20 @@ pub struct AccountId { #[derive(Eq, PartialOrd, Ord, Hash)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct AccountInfo { +pub struct AccountHashUpdate { #[prost(message, optional, tag = "1")] pub account_id: ::core::option::Option, #[prost(message, optional, tag = "2")] pub account_hash: ::core::option::Option, - #[prost(fixed32, tag = "3")] + #[prost(uint32, tag = "3")] pub block_num: u32, } +#[derive(Eq, PartialOrd, Ord, Hash)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccountInfo { + #[prost(message, optional, tag = "1")] + pub update: ::core::option::Option, + #[prost(bytes = "vec", optional, tag = "2")] + pub details: ::core::option::Option<::prost::alloc::vec::Vec>, +} diff --git a/proto/src/generated/requests.rs b/proto/src/generated/requests.rs index 799551253..7bf353f9f 100644 --- a/proto/src/generated/requests.rs +++ b/proto/src/generated/requests.rs @@ -112,3 +112,12 @@ pub struct ListAccountsRequest {} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ListNotesRequest {} +/// Returns the latest state of an account with the specified ID. +#[derive(Eq, PartialOrd, Ord, Hash)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetAccountDetailsRequest { + /// Account ID to get details. + #[prost(message, optional, tag = "1")] + pub account_id: ::core::option::Option, +} diff --git a/proto/src/generated/responses.rs b/proto/src/generated/responses.rs index a0d00f301..553cbbd5a 100644 --- a/proto/src/generated/responses.rs +++ b/proto/src/generated/responses.rs @@ -20,17 +20,6 @@ pub struct GetBlockHeaderByNumberResponse { #[derive(Eq, PartialOrd, Ord, Hash)] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct AccountHashUpdate { - #[prost(message, optional, tag = "1")] - pub account_id: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub account_hash: ::core::option::Option, - #[prost(uint32, tag = "3")] - pub block_num: u32, -} -#[derive(Eq, PartialOrd, Ord, Hash)] -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct NullifierUpdate { #[prost(message, optional, tag = "1")] pub nullifier: ::core::option::Option, @@ -52,7 +41,7 @@ pub struct SyncStateResponse { pub mmr_delta: ::core::option::Option, /// a list of account hashes updated after `block_num + 1` but not after `block_header.block_num` #[prost(message, repeated, tag = "5")] - pub accounts: ::prost::alloc::vec::Vec, + pub accounts: ::prost::alloc::vec::Vec, /// a list of all notes together with the Merkle paths from `block_header.note_root` #[prost(message, repeated, tag = "6")] pub notes: ::prost::alloc::vec::Vec, @@ -92,7 +81,7 @@ pub struct GetBlockInputsResponse { /// Peaks of the above block's mmr, The `forest` value is equal to the block number. #[prost(message, repeated, tag = "2")] pub mmr_peaks: ::prost::alloc::vec::Vec, - /// The hashes of the requested accouts and their authentication paths + /// The hashes of the requested accounts and their authentication paths #[prost(message, repeated, tag = "3")] pub account_states: ::prost::alloc::vec::Vec, /// The requested nullifiers and their authentication paths @@ -158,3 +147,11 @@ pub struct ListNotesResponse { #[prost(message, repeated, tag = "1")] pub notes: ::prost::alloc::vec::Vec, } +#[derive(Eq, PartialOrd, Ord, Hash)] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetAccountDetailsResponse { + /// Account info (with details for on-chain accounts) + #[prost(message, optional, tag = "1")] + pub account: ::core::option::Option, +} diff --git a/proto/src/generated/rpc.rs b/proto/src/generated/rpc.rs index 75d47961e..403d5ea57 100644 --- a/proto/src/generated/rpc.rs +++ b/proto/src/generated/rpc.rs @@ -183,6 +183,32 @@ pub mod api_client { .insert(GrpcMethod::new("rpc.Api", "SubmitProvenTransaction")); self.inner.unary(req, path, codec).await } + pub async fn get_account_details( + &mut self, + request: impl tonic::IntoRequest< + super::super::requests::GetAccountDetailsRequest, + >, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/rpc.Api/GetAccountDetails", + ); + let mut req = request.into_request(); + req.extensions_mut().insert(GrpcMethod::new("rpc.Api", "GetAccountDetails")); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -224,6 +250,13 @@ pub mod api_server { tonic::Response, tonic::Status, >; + async fn get_account_details( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct ApiServer { @@ -501,6 +534,55 @@ pub mod api_server { }; Box::pin(fut) } + "/rpc.Api/GetAccountDetails" => { + #[allow(non_camel_case_types)] + struct GetAccountDetailsSvc(pub Arc); + impl< + T: Api, + > tonic::server::UnaryService< + super::super::requests::GetAccountDetailsRequest, + > for GetAccountDetailsSvc { + type Response = super::super::responses::GetAccountDetailsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::super::requests::GetAccountDetailsRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_account_details(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetAccountDetailsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { Ok( diff --git a/proto/src/generated/store.rs b/proto/src/generated/store.rs index d2f62f986..1820970bc 100644 --- a/proto/src/generated/store.rs +++ b/proto/src/generated/store.rs @@ -299,6 +299,33 @@ pub mod api_client { req.extensions_mut().insert(GrpcMethod::new("store.Api", "ListNotes")); self.inner.unary(req, path, codec).await } + pub async fn get_account_details( + &mut self, + request: impl tonic::IntoRequest< + super::super::requests::GetAccountDetailsRequest, + >, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/store.Api/GetAccountDetails", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("store.Api", "GetAccountDetails")); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -373,6 +400,13 @@ pub mod api_server { tonic::Response, tonic::Status, >; + async fn get_account_details( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct ApiServer { @@ -895,6 +929,55 @@ pub mod api_server { }; Box::pin(fut) } + "/store.Api/GetAccountDetails" => { + #[allow(non_camel_case_types)] + struct GetAccountDetailsSvc(pub Arc); + impl< + T: Api, + > tonic::server::UnaryService< + super::super::requests::GetAccountDetailsRequest, + > for GetAccountDetailsSvc { + type Response = super::super::responses::GetAccountDetailsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::super::requests::GetAccountDetailsRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_account_details(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetAccountDetailsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { Ok( diff --git a/rpc/README.md b/rpc/README.md index 04fef8444..e356c0a98 100644 --- a/rpc/README.md +++ b/rpc/README.md @@ -60,6 +60,19 @@ Retrieves block header by given block number. * `block_header`: `BlockHeader` – block header. +### GetAccountDetails + +Returns the latest state of an account with the specified ID. + +**Parameters** + +* `account_id`: `AccountId` – account ID. + +**Returns** + +* `account`: `AccountInfo` – account state information. For public accounts there is also details describing current state, stored on-chain; + for private accounts only hash of the latest known state is returned. + ### SyncState Returns info which can be used by the client to sync up to the latest state of the chain diff --git a/rpc/src/server/api.rs b/rpc/src/server/api.rs index 9b98f1dd1..60b9b3b8c 100644 --- a/rpc/src/server/api.rs +++ b/rpc/src/server/api.rs @@ -2,18 +2,19 @@ use anyhow::Result; use miden_node_proto::generated::{ block_producer::api_client as block_producer_client, requests::{ - CheckNullifiersRequest, GetBlockHeaderByNumberRequest, SubmitProvenTransactionRequest, - SyncStateRequest, + CheckNullifiersRequest, GetAccountDetailsRequest, GetBlockHeaderByNumberRequest, + SubmitProvenTransactionRequest, SyncStateRequest, }, responses::{ - CheckNullifiersResponse, GetBlockHeaderByNumberResponse, SubmitProvenTransactionResponse, - SyncStateResponse, + CheckNullifiersResponse, GetAccountDetailsResponse, GetBlockHeaderByNumberResponse, + SubmitProvenTransactionResponse, SyncStateResponse, }, rpc::api_server, store::api_client as store_client, }; use miden_objects::{ - transaction::ProvenTransaction, utils::serde::Deserializable, Digest, MIN_PROOF_SECURITY_LEVEL, + accounts::AccountId, transaction::ProvenTransaction, utils::serde::Deserializable, Digest, + MIN_PROOF_SECURITY_LEVEL, }; use miden_tx::TransactionVerifier; use tonic::{ @@ -132,4 +133,30 @@ impl api_server::Api for RpcApi { self.block_producer.clone().submit_proven_transaction(request).await } + + /// Returns details for public (on-chain) account by id. + #[instrument( + target = "miden-rpc", + name = "rpc:get_account_details", + skip_all, + ret(level = "debug"), + err + )] + async fn get_account_details( + &self, + request: Request, + ) -> std::result::Result, Status> { + debug!(target: COMPONENT, request = ?request.get_ref()); + + // Validating account using conversion: + let _account_id: AccountId = request + .get_ref() + .account_id + .clone() + .ok_or(Status::invalid_argument("account_id is missing"))? + .try_into() + .map_err(|err| Status::invalid_argument(format!("Invalid account id: {err}")))?; + + self.store.clone().get_account_details(request).await + } } diff --git a/store/README.md b/store/README.md index a3a95fc69..e0969dae7 100644 --- a/store/README.md +++ b/store/README.md @@ -106,6 +106,19 @@ Returns the data needed by the block producer to check validity of an incoming t * `account_state`: `AccountTransactionInputRecord` – account's descriptors. * `nullifiers`: `[NullifierTransactionInputRecord]` – the block numbers at which corresponding nullifiers have been consumed, zero if not consumed. +### GetAccountDetails + +Returns the latest state of an account with the specified ID. + +**Parameters** + +* `account_id`: `AccountId` – account ID. + +**Returns** + +* `account`: `AccountInfo` – account state information. For public accounts there is also details describing current state, stored on-chain; +for private accounts only hash of the latest known state is returned. + ### SyncState Returns info which can be used by the client to sync up to the latest state of the chain diff --git a/store/src/db/migrations.rs b/store/src/db/migrations.rs index d48b40d7e..1f8d1226d 100644 --- a/store/src/db/migrations.rs +++ b/store/src/db/migrations.rs @@ -11,7 +11,7 @@ pub static MIGRATIONS: Lazy = Lazy::new(|| { block_header BLOB NOT NULL, PRIMARY KEY (block_num), - CONSTRAINT block_header_block_num_is_u32 CHECK (block_num >= 0 AND block_num < 4294967296) + CONSTRAINT block_header_block_num_is_u32 CHECK (block_num BETWEEN 0 AND 0xFFFFFFFF) ) STRICT, WITHOUT ROWID; CREATE TABLE @@ -27,9 +27,9 @@ pub static MIGRATIONS: Lazy = Lazy::new(|| { PRIMARY KEY (block_num, batch_index, note_index), CONSTRAINT fk_block_num FOREIGN KEY (block_num) REFERENCES block_headers (block_num), - CONSTRAINT notes_block_number_is_u32 CHECK (block_num >= 0 AND block_num < 4294967296), + CONSTRAINT notes_block_num_is_u32 CHECK (block_num BETWEEN 0 AND 0xFFFFFFFF), CONSTRAINT notes_batch_index_is_u32 CHECK (batch_index BETWEEN 0 AND 0xFFFFFFFF) - CONSTRAINT notes_note_index_is_u32 CHECK (note_index >= 0 AND note_index < 4294967296) + CONSTRAINT notes_note_index_is_u32 CHECK (note_index BETWEEN 0 AND 0xFFFFFFFF) ) STRICT, WITHOUT ROWID; CREATE TABLE @@ -38,10 +38,11 @@ pub static MIGRATIONS: Lazy = Lazy::new(|| { account_id INTEGER NOT NULL, account_hash BLOB NOT NULL, block_num INTEGER NOT NULL, + details BLOB, PRIMARY KEY (account_id), CONSTRAINT fk_block_num FOREIGN KEY (block_num) REFERENCES block_headers (block_num), - CONSTRAINT accounts_block_num_is_u32 CHECK (block_num >= 0 AND block_num < 4294967296) + CONSTRAINT accounts_block_num_is_u32 CHECK (block_num BETWEEN 0 AND 0xFFFFFFFF) ) STRICT, WITHOUT ROWID; CREATE TABLE @@ -49,13 +50,13 @@ pub static MIGRATIONS: Lazy = Lazy::new(|| { ( nullifier BLOB NOT NULL, nullifier_prefix INTEGER NOT NULL, - block_number INTEGER NOT NULL, + block_num INTEGER NOT NULL, PRIMARY KEY (nullifier), - CONSTRAINT fk_block_num FOREIGN KEY (block_number) REFERENCES block_headers (block_num), + CONSTRAINT fk_block_num FOREIGN KEY (block_num) REFERENCES block_headers (block_num), CONSTRAINT nullifiers_nullifier_is_digest CHECK (length(nullifier) = 32), - CONSTRAINT nullifiers_nullifier_prefix_is_u16 CHECK (nullifier_prefix >= 0 AND nullifier_prefix < 65536), - CONSTRAINT nullifiers_block_number_is_u32 CHECK (block_number >= 0 AND block_number < 4294967296) + CONSTRAINT nullifiers_nullifier_prefix_is_u16 CHECK (nullifier_prefix BETWEEN 0 AND 0xFFFF), + CONSTRAINT nullifiers_block_num_is_u32 CHECK (block_num BETWEEN 0 AND 0xFFFFFFFF) ) STRICT, WITHOUT ROWID; ", )]) diff --git a/store/src/db/mod.rs b/store/src/db/mod.rs index e06873e39..b448e9093 100644 --- a/store/src/db/mod.rs +++ b/store/src/db/mod.rs @@ -1,10 +1,12 @@ use std::fs::{self, create_dir_all}; use deadpool_sqlite::{Config as SqliteConfig, Hook, HookError, Pool, Runtime}; +use miden_node_proto::domain::accounts::{AccountHashUpdate, AccountInfo}; use miden_objects::{ block::BlockNoteTree, crypto::{hash::rpo::RpoDigest, merkle::MerklePath, utils::Deserializable}, notes::Nullifier, + transaction::AccountDetails, BlockHeader, GENESIS_BLOCK, }; use rusqlite::vtab::array; @@ -31,13 +33,6 @@ pub struct Db { pool: Pool, } -#[derive(Debug, PartialEq)] -pub struct AccountInfo { - pub account_id: AccountId, - pub account_hash: RpoDigest, - pub block_num: BlockNumber, -} - #[derive(Debug, PartialEq)] pub struct NullifierInfo { pub nullifier: Nullifier, @@ -73,7 +68,7 @@ pub struct StateSyncUpdate { pub notes: Vec, pub block_header: BlockHeader, pub chain_tip: BlockNumber, - pub account_updates: Vec, + pub account_updates: Vec, pub nullifiers: Vec, } @@ -209,6 +204,22 @@ impl Db { })? } + /// Loads public account details from the DB. + #[instrument(target = "miden-store", skip_all, ret(level = "debug"), err)] + pub async fn select_account( + &self, + id: AccountId, + ) -> Result { + self.pool + .get() + .await? + .interact(move |conn| sql::select_account(conn, id)) + .await + .map_err(|err| { + DatabaseError::InteractError(format!("Get account details task failed: {err}")) + })? + } + #[instrument(target = "miden-store", skip_all, ret(level = "debug"), err)] pub async fn get_state_sync( &self, @@ -253,7 +264,7 @@ impl Db { block_header: BlockHeader, notes: Vec, nullifiers: Vec, - accounts: Vec<(AccountId, RpoDigest)>, + accounts: Vec<(AccountId, Option, RpoDigest)>, ) -> Result<()> { self.pool .get() @@ -336,7 +347,7 @@ impl Db { let transaction = conn.transaction()?; let accounts: Vec<_> = account_smt .leaves() - .map(|(account_id, state_hash)| (account_id, state_hash.into())) + .map(|(account_id, state_hash)| (account_id, None, state_hash.into())) .collect(); sql::apply_block( &transaction, diff --git a/store/src/db/sql.rs b/store/src/db/sql.rs index ff39c06bb..2088f1382 100644 --- a/store/src/db/sql.rs +++ b/store/src/db/sql.rs @@ -1,15 +1,18 @@ //! Wrapper functions for SQL statements. use std::rc::Rc; +use miden_node_proto::domain::accounts::{AccountHashUpdate, AccountInfo}; use miden_objects::{ - crypto::hash::rpo::RpoDigest, + accounts::Account, + crypto::{hash::rpo::RpoDigest, merkle::MerklePath}, notes::Nullifier, + transaction::AccountDetails, utils::serde::{Deserializable, Serializable}, BlockHeader, }; use rusqlite::{params, types::Value, Connection, Transaction}; -use super::{AccountInfo, Note, NoteCreated, NullifierInfo, Result, StateSyncUpdate}; +use super::{Note, NoteCreated, NullifierInfo, Result, StateSyncUpdate}; use crate::{ errors::{DatabaseError, StateSyncError}, types::{AccountId, BlockNumber}, @@ -24,21 +27,24 @@ use crate::{ /// /// A vector with accounts, or an error. pub fn select_accounts(conn: &mut Connection) -> Result> { - let mut stmt = conn.prepare("SELECT * FROM accounts ORDER BY block_num ASC;")?; + let mut stmt = conn.prepare( + " + SELECT + account_id, + account_hash, + block_num, + details + FROM + accounts + ORDER BY + block_num ASC; + ", + )?; let mut rows = stmt.query([])?; let mut accounts = vec![]; while let Some(row) = rows.next()? { - let account_hash_data = row.get_ref(1)?.as_blob()?; - let account_hash = deserialize(account_hash_data)?; - let account_id = column_value_as_u64(row, 0)?; - let block_num = row.get(2)?; - - accounts.push(AccountInfo { - account_id, - account_hash, - block_num, - }) + accounts.push(account_info_from_row(row)?) } Ok(accounts) } @@ -57,7 +63,7 @@ pub fn select_account_hashes(conn: &mut Connection) -> Result Result> { +) -> Result> { let account_ids: Vec = account_ids.iter().copied().map(u64_to_value).collect(); let mut stmt = conn.prepare( " SELECT - account_id, account_hash, block_num + account_id, + account_hash, + block_num FROM accounts WHERE @@ -98,19 +106,39 @@ pub fn select_accounts_by_block_range( let mut result = Vec::new(); while let Some(row) = rows.next()? { - let account_id = column_value_as_u64(row, 0)?; - let account_hash_data = row.get_ref(1)?.as_blob()?; - let account_hash = deserialize(account_hash_data)?; - let block_num = row.get(2)?; + result.push(account_hash_update_from_row(row)?) + } - result.push(AccountInfo { + Ok(result) +} + +/// Select the latest account details by account id from the DB using the given [Connection]. +/// +/// # Returns +/// +/// The latest account details, or an error. +pub fn select_account( + conn: &mut Connection, + account_id: AccountId, +) -> Result { + let mut stmt = conn.prepare( + " + SELECT account_id, account_hash, block_num, - }); - } + details + FROM + accounts + WHERE + account_id = ?1; + ", + )?; - Ok(result) + let mut rows = stmt.query(params![u64_to_value(account_id)])?; + let row = rows.next()?.ok_or(DatabaseError::AccountNotFoundInDb(account_id))?; + + account_info_from_row(row) } /// Inserts or updates accounts to the DB using the given [Transaction]. @@ -125,15 +153,27 @@ pub fn select_accounts_by_block_range( /// transaction. pub fn upsert_accounts( transaction: &Transaction, - accounts: &[(AccountId, RpoDigest)], + accounts: &[(AccountId, Option, RpoDigest)], block_num: BlockNumber, ) -> Result { - let mut stmt = transaction.prepare("INSERT OR REPLACE INTO accounts (account_id, account_hash, block_num) VALUES (?1, ?2, ?3);")?; + let mut stmt = transaction.prepare( + " + INSERT OR REPLACE INTO + accounts (account_id, account_hash, block_num, details) + VALUES (?1, ?2, ?3, ?4); + ", + )?; let mut count = 0; - for (account_id, account_hash) in accounts.iter() { - count += - stmt.execute(params![u64_to_value(*account_id), account_hash.to_bytes(), block_num])? + for (account_id, details, account_hash) in accounts.iter() { + // TODO: Process account details/delta (in the next PR) + + count += stmt.execute(params![ + u64_to_value(*account_id), + account_hash.to_bytes(), + block_num, + details.as_ref().map(|details| details.to_bytes()), + ])? } Ok(count) } @@ -157,7 +197,7 @@ pub fn insert_nullifiers_for_block( block_num: BlockNumber, ) -> Result { let mut stmt = transaction.prepare( - "INSERT INTO nullifiers (nullifier, nullifier_prefix, block_number) VALUES (?1, ?2, ?3);", + "INSERT INTO nullifiers (nullifier, nullifier_prefix, block_num) VALUES (?1, ?2, ?3);", )?; let mut count = 0; @@ -175,13 +215,13 @@ pub fn insert_nullifiers_for_block( /// A vector with nullifiers and the block height at which they were created, or an error. pub fn select_nullifiers(conn: &mut Connection) -> Result> { let mut stmt = - conn.prepare("SELECT nullifier, block_number FROM nullifiers ORDER BY block_number ASC;")?; + conn.prepare("SELECT nullifier, block_num FROM nullifiers ORDER BY block_num ASC;")?; let mut rows = stmt.query([])?; let mut result = vec![]; while let Some(row) = rows.next()? { let nullifier_data = row.get_ref(0)?.as_blob()?; - let nullifier = deserialize(nullifier_data)?; + let nullifier = Nullifier::read_from_bytes(nullifier_data)?; let block_number = row.get(1)?; result.push((nullifier, block_number)); } @@ -211,15 +251,15 @@ pub fn select_nullifiers_by_block_range( " SELECT nullifier, - block_number + block_num FROM nullifiers WHERE - block_number > ?1 AND - block_number <= ?2 AND + block_num > ?1 AND + block_num <= ?2 AND nullifier_prefix IN rarray(?3) ORDER BY - block_number ASC + block_num ASC ", )?; @@ -228,7 +268,7 @@ pub fn select_nullifiers_by_block_range( let mut result = Vec::new(); while let Some(row) = rows.next()? { let nullifier_data = row.get_ref(0)?.as_blob()?; - let nullifier = deserialize(nullifier_data)?; + let nullifier = Nullifier::read_from_bytes(nullifier_data)?; let block_num = row.get(1)?; result.push(NullifierInfo { nullifier, @@ -269,10 +309,10 @@ pub fn select_notes(conn: &mut Connection) -> Result> { let mut notes = vec![]; while let Some(row) = rows.next()? { let note_id_data = row.get_ref(3)?.as_blob()?; - let note_id = deserialize(note_id_data)?; + let note_id = RpoDigest::read_from_bytes(note_id_data)?; let merkle_path_data = row.get_ref(6)?.as_blob()?; - let merkle_path = deserialize(merkle_path_data)?; + let merkle_path = MerklePath::read_from_bytes(merkle_path_data)?; notes.push(Note { block_num: row.get(0)?, @@ -398,11 +438,11 @@ pub fn select_notes_since_block_by_tag_and_sender( let batch_index = row.get(1)?; let note_index = row.get(2)?; let note_id_data = row.get_ref(3)?.as_blob()?; - let note_id = deserialize(note_id_data)?; + let note_id = RpoDigest::read_from_bytes(note_id_data)?; let sender = column_value_as_u64(row, 4)?; let tag = column_value_as_u64(row, 5)?; let merkle_path_data = row.get_ref(6)?.as_blob()?; - let merkle_path = deserialize(merkle_path_data)?; + let merkle_path = MerklePath::read_from_bytes(merkle_path_data)?; let note = Note { block_num, @@ -469,7 +509,7 @@ pub fn select_block_header_by_block_num( match rows.next()? { Some(row) => { let data = row.get_ref(0)?.as_blob()?; - Ok(Some(deserialize(data)?)) + Ok(Some(BlockHeader::read_from_bytes(data)?)) }, None => Ok(None), } @@ -487,7 +527,7 @@ pub fn select_block_headers(conn: &mut Connection) -> Result> { let mut result = vec![]; while let Some(row) = rows.next()? { let block_header_data = row.get_ref(0)?.as_blob()?; - let block_header = deserialize(block_header_data)?; + let block_header = BlockHeader::read_from_bytes(block_header_data)?; result.push(block_header); } @@ -559,7 +599,7 @@ pub fn apply_block( block_header: &BlockHeader, notes: &[Note], nullifiers: &[Nullifier], - accounts: &[(AccountId, RpoDigest)], + accounts: &[(AccountId, Option, RpoDigest)], ) -> Result { let mut count = 0; count += insert_block_header(transaction, block_header)?; @@ -572,11 +612,6 @@ pub fn apply_block( // UTILITIES // ================================================================================================ -/// Decodes a blob from the database into a corresponding deserializable. -fn deserialize(data: &[u8]) -> Result { - T::read_from_bytes(data).map_err(DatabaseError::DeserializationError) -} - /// Returns the high 16 bits of the provided nullifier. pub(crate) fn get_nullifier_prefix(nullifier: &Nullifier) -> u32 { (nullifier.most_significant_felt().as_int() >> 48) as u32 @@ -609,3 +644,31 @@ fn column_value_as_u64( let value: i64 = row.get(index)?; Ok(value as u64) } + +/// Constructs `AccountHashUpdate` from the row of `accounts` table. +/// +/// Note: field ordering must be the same, as in `accounts` table! +fn account_hash_update_from_row(row: &rusqlite::Row<'_>) -> Result { + let account_id = column_value_as_u64(row, 0)?; + let account_hash_data = row.get_ref(1)?.as_blob()?; + let account_hash = RpoDigest::read_from_bytes(account_hash_data)?; + let block_num = row.get(2)?; + + Ok(AccountHashUpdate { + account_id: account_id.try_into()?, + account_hash, + block_num, + }) +} + +/// Constructs `AccountInfo` from the row of `accounts` table. +/// +/// Note: field ordering must be the same, as in `accounts` table! +fn account_info_from_row(row: &rusqlite::Row<'_>) -> Result { + let update = account_hash_update_from_row(row)?; + + let details = row.get_ref(3)?.as_bytes_or_null()?; + let details = details.map(Account::read_from_bytes).transpose()?; + + Ok(AccountInfo { update, details }) +} diff --git a/store/src/db/tests.rs b/store/src/db/tests.rs index 4658ef70c..61fc90cd8 100644 --- a/store/src/db/tests.rs +++ b/store/src/db/tests.rs @@ -1,5 +1,8 @@ +use miden_node_proto::domain::accounts::AccountHashUpdate; use miden_objects::{ - accounts::{AccountId, ACCOUNT_ID_OFF_CHAIN_SENDER}, + accounts::{ + AccountId, ACCOUNT_ID_OFF_CHAIN_SENDER, ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN, + }, block::BlockNoteTree, crypto::{hash::rpo::RpoDigest, merkle::MerklePath}, notes::{NoteMetadata, NoteType, Nullifier}, @@ -161,16 +164,21 @@ fn test_sql_select_accounts() { // test multiple entries let mut state = vec![]; for i in 0..10 { - let account_id = i; + let account_id = + ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN + (i << 32) + 0b1111100000; let account_hash = num_to_rpo_digest(i); state.push(AccountInfo { - account_id, - account_hash, - block_num, + update: AccountHashUpdate { + account_id: account_id.try_into().unwrap(), + account_hash, + block_num, + }, + details: None, }); let transaction = conn.transaction().unwrap(); - let res = sql::upsert_accounts(&transaction, &[(account_id, account_hash)], block_num); + let res = + sql::upsert_accounts(&transaction, &[(account_id, None, account_hash)], block_num); assert_eq!(res.unwrap(), 1, "One element must have been inserted"); transaction.commit().unwrap(); let accounts = sql::select_accounts(&mut conn).unwrap(); @@ -375,17 +383,17 @@ fn test_db_account() { create_block(&mut conn, block_num); // test empty table - let account_ids = vec![0, 1, 2, 3, 4, 5]; + let account_ids = vec![ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN, 1, 2, 3, 4, 5]; let res = sql::select_accounts_by_block_range(&mut conn, 0, u32::MAX, &account_ids).unwrap(); assert!(res.is_empty()); // test insertion - let account_id = 0; + let account_id = ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN; let account_hash = num_to_rpo_digest(0); let transaction = conn.transaction().unwrap(); let row_count = - sql::upsert_accounts(&transaction, &[(account_id, account_hash)], block_num).unwrap(); + sql::upsert_accounts(&transaction, &[(account_id, None, account_hash)], block_num).unwrap(); transaction.commit().unwrap(); assert_eq!(row_count, 1); @@ -394,10 +402,10 @@ fn test_db_account() { let res = sql::select_accounts_by_block_range(&mut conn, 0, u32::MAX, &account_ids).unwrap(); assert_eq!( res, - vec![AccountInfo { - account_id, + vec![AccountHashUpdate { + account_id: account_id.try_into().unwrap(), account_hash, - block_num + block_num, }] ); @@ -432,7 +440,6 @@ fn test_notes() { let note_index = 2u32; let note_id = num_to_rpo_digest(3); let tag = 5u64; - // Precomputed seed for regular off-chain account for zeroed initial seed: let sender = AccountId::new_unchecked(Felt::new(ACCOUNT_ID_OFF_CHAIN_SENDER)); let note_metadata = NoteMetadata::new(sender, NoteType::OffChain, (tag as u32).into(), ZERO).unwrap(); diff --git a/store/src/errors.rs b/store/src/errors.rs index 3263333e5..4aa2d27fd 100644 --- a/store/src/errors.rs +++ b/store/src/errors.rs @@ -13,7 +13,7 @@ use rusqlite::types::FromSqlError; use thiserror::Error; use tokio::sync::oneshot::error::RecvError; -use crate::types::BlockNumber; +use crate::types::{AccountId, BlockNumber}; // INTERNAL ERRORS // ================================================================================================= @@ -42,12 +42,22 @@ pub enum DatabaseError { FromSqlError(#[from] FromSqlError), #[error("I/O error: {0}")] IoError(#[from] io::Error), + #[error("Account error: {0}")] + AccountError(#[from] AccountError), #[error("SQLite pool interaction task failed: {0}")] InteractError(String), #[error("Deserialization of BLOB data from database failed: {0}")] DeserializationError(DeserializationError), #[error("Block applying was broken because of closed channel on state side: {0}")] ApplyBlockFailedClosedChannel(RecvError), + #[error("Account {0} not found in the database")] + AccountNotFoundInDb(AccountId), +} + +impl From for DatabaseError { + fn from(value: DeserializationError) -> Self { + Self::DeserializationError(value) + } } // INITIALIZATION ERRORS diff --git a/store/src/server/api.rs b/store/src/server/api.rs index f0f24ac59..c4b8fa3af 100644 --- a/store/src/server/api.rs +++ b/store/src/server/api.rs @@ -5,15 +5,16 @@ use miden_node_proto::{ errors::ConversionError, generated::{ self, + account::AccountHashUpdate, note::NoteSyncRecord, requests::{ - ApplyBlockRequest, CheckNullifiersRequest, GetBlockHeaderByNumberRequest, - GetBlockInputsRequest, GetTransactionInputsRequest, ListAccountsRequest, - ListNotesRequest, ListNullifiersRequest, SyncStateRequest, + ApplyBlockRequest, CheckNullifiersRequest, GetAccountDetailsRequest, + GetBlockHeaderByNumberRequest, GetBlockInputsRequest, GetTransactionInputsRequest, + ListAccountsRequest, ListNotesRequest, ListNullifiersRequest, SyncStateRequest, }, responses::{ - AccountHashUpdate, AccountTransactionInputRecord, ApplyBlockResponse, - CheckNullifiersResponse, GetBlockHeaderByNumberResponse, GetBlockInputsResponse, + AccountTransactionInputRecord, ApplyBlockResponse, CheckNullifiersResponse, + GetAccountDetailsResponse, GetBlockHeaderByNumberResponse, GetBlockInputsResponse, GetTransactionInputsResponse, ListAccountsResponse, ListNotesResponse, ListNullifiersResponse, NullifierTransactionInputRecord, NullifierUpdate, SyncStateResponse, @@ -159,6 +160,32 @@ impl api_server::Api for StoreApi { })) } + /// Returns details for public (on-chain) account by id. + #[instrument( + target = "miden-store", + name = "store:get_account_details", + skip_all, + ret(level = "debug"), + err + )] + async fn get_account_details( + &self, + request: tonic::Request, + ) -> Result, Status> { + let request = request.into_inner(); + let account_info = self + .state + .get_account_details( + request.account_id.ok_or(invalid_argument("Account missing id"))?.into(), + ) + .await + .map_err(internal_error)?; + + Ok(Response::new(GetAccountDetailsResponse { + account: Some((&account_info).into()), + })) + } + // BLOCK PRODUCER ENDPOINTS // -------------------------------------------------------------------------------------------- @@ -195,6 +222,7 @@ impl api_server::Api for StoreApi { .map_err(|err: ConversionError| Status::invalid_argument(err.to_string()))?; Ok(( account_state.account_id.into(), + None, // TODO: Process account details (next PR) account_state .account_hash .ok_or(invalid_argument("Account update missing account hash"))?, @@ -367,12 +395,8 @@ impl api_server::Api for StoreApi { .list_accounts() .await .map_err(internal_error)? - .into_iter() - .map(|account_info| generated::account::AccountInfo { - account_id: Some(account_info.account_id.into()), - account_hash: Some(account_info.account_hash.into()), - block_num: account_info.block_num, - }) + .iter() + .map(Into::into) .collect(); Ok(Response::new(ListAccountsResponse { accounts })) } diff --git a/store/src/state.rs b/store/src/state.rs index 85099f7f5..aa18ccc71 100644 --- a/store/src/state.rs +++ b/store/src/state.rs @@ -4,7 +4,7 @@ //! data is atomically written, and that reads are consistent. use std::{mem, sync::Arc}; -use miden_node_proto::{AccountInputRecord, NullifierWitness}; +use miden_node_proto::{domain::accounts::AccountInfo, AccountInputRecord, NullifierWitness}; use miden_node_utils::formatting::{format_account_id, format_array}; use miden_objects::{ block::BlockNoteTree, @@ -13,6 +13,7 @@ use miden_objects::{ merkle::{LeafIndex, Mmr, MmrDelta, MmrPeaks, SimpleSmt, SmtProof, ValuePath}, }, notes::{NoteMetadata, NoteType, Nullifier}, + transaction::AccountDetails, AccountError, BlockHeader, NoteError, ACCOUNT_TREE_DEPTH, ZERO, }; use tokio::{ @@ -22,7 +23,7 @@ use tokio::{ use tracing::{error, info, info_span, instrument}; use crate::{ - db::{AccountInfo, Db, Note, NoteCreated, NullifierInfo, StateSyncUpdate}, + db::{Db, Note, NoteCreated, NullifierInfo, StateSyncUpdate}, errors::{ ApplyBlockError, DatabaseError, GetBlockInputsError, StateInitializationError, StateSyncError, @@ -106,7 +107,7 @@ impl State { &self, block_header: BlockHeader, nullifiers: Vec, - accounts: Vec<(AccountId, RpoDigest)>, + accounts: Vec<(AccountId, Option, RpoDigest)>, notes: Vec, ) -> Result<(), ApplyBlockError> { let _ = self.writer.try_lock().map_err(|_| ApplyBlockError::ConcurrentWrite)?; @@ -180,7 +181,7 @@ impl State { // update account tree let mut account_tree = inner.account_tree.clone(); - for (account_id, account_hash) in accounts.iter() { + for (account_id, _details, account_hash) in accounts.iter() { account_tree.insert(LeafIndex::new_max_depth(*account_id), account_hash.into()); } @@ -463,6 +464,14 @@ impl State { pub async fn list_notes(&self) -> Result, DatabaseError> { self.db.select_notes().await } + + /// Returns details for public (on-chain) account. + pub async fn get_account_details( + &self, + id: AccountId, + ) -> Result { + self.db.select_account(id).await + } } // UTILITIES