diff --git a/CHANGELOG.md b/CHANGELOG.md index cf34f1519..986374434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,17 @@ ## 0.6.0 (TBD) - Made note scripts public (#880). +- Implemented serialization for `TransactionWitness`, `ChainMmr`, `TransactionInputs` and `TransactionArgs` (#888). - [BREAKING] Renamed the `TransactionProver` struct to `LocalTransactionProver` and added the `TransactionProver` trait (#865). - Implemented `Display`, `TryFrom<&str>` and `FromStr` for `AccountStorageMode` (#861). - Implemented offset based storage access (#843). - [BREAKING] `AccountStorageType` enum was renamed to `AccountStorageMode` along with its variants (#854). - [BREAKING] `AccountStub` structure was renamed to `AccountHeader` (#855). - [BREAKING] Kernel procedures now have to be invoked using `dynexec` instruction (#803). -- Refactored `AccountStorage` from `Smt` to `sequential hash` (#846) +- Refactored `AccountStorage` from `Smt` to `sequential hash` (#846). - [BREAKING] Refactored batch/block note trees (#834). +- Set all procedures storage offsets of faucet accounts to `1` (#875). +- Added `AccountStorageHeader` (#876). ## 0.5.1 (2024-08-28) - `miden-objects` crate only @@ -43,6 +46,7 @@ - Added serialization and equality comparison for `TransactionScript` (#824). - [BREAKING] Migrated to Miden VM v0.10 (#826). - Added conversions for `NoteExecutionHint` (#827). +- [BREAKING] Removed `serde`-based serialization from `miden-object` structs (#838). ## 0.4.0 (2024-07-03) diff --git a/Cargo.lock b/Cargo.lock index 03fa7b080..3fba7d796 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -40,9 +40,9 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -67,17 +67,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -146,9 +146,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.16" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", @@ -221,9 +221,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -792,9 +792,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6fad06fc3af260ed3c4235821daa2132813d993f96d446856036ae97e9606dd" +checksum = "8a69f8362ca496a79c88cf8e5b9b349bf9c6ed49fa867d0548e670afc1f3fca5" dependencies = [ "blake3", "cc", @@ -803,7 +803,6 @@ dependencies = [ "num-complex", "rand", "rand_core", - "serde", "sha3", "winter-crypto", "winter-math", @@ -885,16 +884,15 @@ dependencies = [ "miden-verifier", "rand", "rstest", - "serde", "tempfile", "winter-rand-utils", ] [[package]] name = "miden-processor" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e7b212b152b69373e89b069a18cb01742ef2c3f9c328e7b24c44e44f022e52" +checksum = "04a128e20400086c9a985f4e5702e438ba781338fb0bdf9acff16d996c640087" dependencies = [ "miden-air", "miden-core", @@ -972,11 +970,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1091,9 +1089,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" [[package]] name = "oorandom" @@ -1109,9 +1107,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owo-colors" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "parking_lot" @@ -1261,9 +1259,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] @@ -1385,9 +1383,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -1452,18 +1450,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1730,9 +1728,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "serde", @@ -1825,9 +1823,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-linebreak" @@ -2195,7 +2193,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004f85bb051ce986ec0b9a2bd90aaf81b83e3c67464becfdf7db31f14c1019ba" dependencies = [ - "serde", "winter-utils", ] @@ -2247,9 +2244,9 @@ dependencies = [ [[package]] name = "winter-utils" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0568612a95bcae3c94fb14da2686f8279ca77723dbdf1e97cf3673798faf6485" +checksum = "c3d71ec2c97685c7e7460a30e27f955d26b8426e7c2db0ddb55a6e0537141f53" dependencies = [ "rayon", ] diff --git a/Makefile b/Makefile index bf1494fec..ff12b5d88 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ help: WARNINGS=RUSTDOCFLAGS="-D warnings" DEBUG_ASSERTIONS=RUSTFLAGS="-C debug-assertions" -ALL_FEATURES_BUT_ASYNC=--features concurrent,testing,serde +ALL_FEATURES_BUT_ASYNC=--features concurrent,testing # -- linting -------------------------------------------------------------------------------------- diff --git a/bench-tx/src/utils.rs b/bench-tx/src/utils.rs index e02406b7f..0f2e00f85 100644 --- a/bench-tx/src/utils.rs +++ b/bench-tx/src/utils.rs @@ -76,7 +76,7 @@ pub fn get_account_with_default_account_code( let account_code_src = DEFAULT_ACCOUNT_CODE; let assembler = TransactionKernel::assembler(); - let account_code = AccountCode::compile(account_code_src, assembler).unwrap(); + let account_code = AccountCode::compile(account_code_src, assembler, false).unwrap(); let account_storage = AccountStorage::new(vec![StorageSlot::Value(public_key)]).unwrap(); let account_vault = match assets { diff --git a/miden-lib/asm/kernels/transaction/lib/account.masm b/miden-lib/asm/kernels/transaction/lib/account.masm index 7a65f0050..203346f8f 100644 --- a/miden-lib/asm/kernels/transaction/lib/account.masm +++ b/miden-lib/asm/kernels/transaction/lib/account.masm @@ -37,6 +37,9 @@ const.ERR_PROC_INDEX_OUT_OF_BOUNDS=0x0002004B # Provided storage slot index is out of bounds const.ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS=0x0002004E +# Storage offset is invalid for a faucet account (0 is prohibited being the faucet reserved data slot) +const.ERR_INVALID_FAUCET_STORAGE_OFFSET=0x0002004F + # CONSTANTS # ================================================================================================= @@ -201,25 +204,6 @@ export.memory::get_acct_nonce->get_nonce #! - H is the initial account hash. export.memory::get_init_acct_hash->get_initial_hash -#! Returns the most significant half with the account type bits masked out. -#! -#! The account type can be defined by comparing this value with the following constants: -#! -#! - REGULAR_ACCOUNT_UPDATABLE_CODE -#! - REGULAR_ACCOUNT_IMMUTABLE_CODE -#! - FUNGIBLE_FAUCET_ACCOUNT -#! - NON_FUNGIBLE_FAUCET_ACCOUNT -#! -#! Stack: [acct_id] -#! Output: [acct_type] -#! -#! - acct_id is the account id. -#! - acct_type is the account type. -proc.type - u32split swap drop push.ACCOUNT_TYPE_U32MASK u32and - # => [acct_type] -end - #! Returns a boolean indicating whether the account is a fungible faucet. #! #! Stack: [acct_id] @@ -341,6 +325,83 @@ export.apply_storage_offset # => [offset_slot_index] end +#! Validates all account procedures storage offsets by checking that: +#! - All storage offsets are in bounds +#! - All storage offsets adhere to account type specific rules +#! +#! Notes: +#! - For faucets checks that no storage offset is 0 (preventing reserved storage slot access) +#! +#! Stack: [] +#! Output: [] +export.validate_storage_offsets + # get number of account procedures and number of storage slots + exec.memory::get_num_account_procedures exec.memory::get_num_storage_slots + # => [num_storage_slots, num_account_procedures] + + # prepare stack for looping + push.0.1 + # => [start_loop, index, num_storage_slots, num_account_procedures] + + # check if the account is a faucet + exec.get_id exec.is_faucet + # => [is_faucet, start_loop, index, num_storage_slots, num_account_procedures] + + # we do not check if num_account_procedures == 0 here because a valid + # account has between 1 and 256 procedures with associated offsets + if.true + while.true + # get storage offset from memory + dup exec.get_procedure_storage_offset + # => [storage_offset, index, num_storage_slots, num_account_procedures] + + # assert that storage offset is not 0 + dup push.0 neq assert.err=ERR_INVALID_FAUCET_STORAGE_OFFSET + # => [storage_offset, index, num_storage_slots, num_account_procedures] + + # TODO: This is a temporary check for faucets. We add 1 to all faucet offsets + # to prevent access to the reserved faucet data slot. When the assembler + # will support storage offsets remove this check. + # check if the storage offset and the number of storage slots are 1 + dup dup.3 eq.1 swap eq.1 and + # => [not_both_1, storage_offset, index, num_storage_slots, num_account_procedures] + + if.false + # assert that storage offset is in bounds + dup.2 lt assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS + # => [index, num_storage_slots, num_account_procedures] + else + # TODO: Remove this drop with the check above + # skip bound check and drop storage_offset + drop + # => [index, num_storage_slots, num_account_procedures] + end + + # check if we should continue looping + add.1 dup dup.3 lt + # => [should_loop, index, num_storage_slots, num_account_procedures] + end + else + while.true + # get storage offset from memory + dup exec.get_procedure_storage_offset + # => [storage_offset, index, num_storage_slots, num_account_procedures] + + # assert that storage offset is in bounds + dup.2 lt assert.err=ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS + # => [index, num_storage_slots, num_account_procedures] + + # check if we should continue looping + add.1 dup dup.3 lt + # => [should_loop, index, num_storage_slots, num_account_procedures] + end + end + + # clean stack + drop drop drop + # => [] +end + #! Gets an item from the account storage #! #! Note: @@ -361,36 +422,6 @@ export.get_item # => [VALUE] end -#! Sets an item in the account storage. Doesn't emit any events. -#! -#! Stack: [index, NEW_VALUE] -#! Output: [OLD_VALUE] -#! -#! - index is the index of the item to set. -#! - NEW_VALUE is the value to set. -#! - OLD_VALUE is the previous value of the item. -proc.set_item_raw - # get old value from storage - dup movdn.5 exec.get_item - # => [OLD_VALUE, NEW_VALUE, index] - - # arrange stack for storage update - swapw movup.8 - # => [index, NEW_VALUE, OLD_VALUE] - - # get account storage slots section offset - exec.memory::get_acct_storage_slots_section_offset - # => [acct_storage_slots_section_offset, index, NEW_VALUE, OLD_VALUE] - - # update storage - swap mul.2 add mem_storew - # => [NEW_VALUE, OLD_VALUE] - - # drop value - dropw - # => [OLD_VALUE] -end - #! Sets an item in the account storage. #! #! Note: @@ -536,14 +567,14 @@ export.get_procedure_info dup exec.memory::get_num_account_procedures lt assert.err=ERR_PROC_INDEX_OUT_OF_BOUNDS # => [index] - # get procedure section ptr - push.2 mul exec.memory::get_acct_procedures_section_offset add dup push.1 add - # => [proc_ptr, offset_ptr] + # get procedure pointer + mul.2 exec.memory::get_acct_procedures_section_offset add dup add.1 + # => [offset_ptr, proc_ptr] # load procedure information from memory mem_load swap padw movup.4 mem_loadw # => [PROC_ROOT, storage_offset] - end +end #! Verifies that the procedure root is part of the account code #! @@ -630,3 +661,74 @@ export.validate_seed u32split drop swap u32divmod assertz.err=ERR_ACCOUNT_INVALID_POW drop # => [] end + +# HELPER PROCEDURES +# ================================================================================================= + +#! Returns the most significant half with the account type bits masked out. +#! +#! The account type can be defined by comparing this value with the following constants: +#! +#! - REGULAR_ACCOUNT_UPDATABLE_CODE +#! - REGULAR_ACCOUNT_IMMUTABLE_CODE +#! - FUNGIBLE_FAUCET_ACCOUNT +#! - NON_FUNGIBLE_FAUCET_ACCOUNT +#! +#! Stack: [acct_id] +#! Output: [acct_type] +#! +#! - acct_id is the account id. +#! - acct_type is the account type. +proc.type + u32split swap drop push.ACCOUNT_TYPE_U32MASK u32and + # => [acct_type] +end + +#! Sets an item in the account storage. Doesn't emit any events. +#! +#! Stack: [index, NEW_VALUE] +#! Output: [OLD_VALUE] +#! +#! - index is the index of the item to set. +#! - NEW_VALUE is the value to set. +#! - OLD_VALUE is the previous value of the item. +proc.set_item_raw + # get old value from storage + dup movdn.5 exec.get_item + # => [OLD_VALUE, NEW_VALUE, index] + + # arrange stack for storage update + swapw movup.8 + # => [index, NEW_VALUE, OLD_VALUE] + + # get account storage slots section offset + exec.memory::get_acct_storage_slots_section_offset + # => [acct_storage_slots_section_offset, index, NEW_VALUE, OLD_VALUE] + + # update storage + swap mul.2 add mem_storew + # => [NEW_VALUE, OLD_VALUE] + + # drop value + dropw + # => [OLD_VALUE] +end + +#! Returns the procedure storage offset +#! +#! Note: +#! - We assume that index has been validated and is within bounds +#! +#! Stack: [index, ...] +#! Output: [storage_offset, ...] +#! +#! - storage_offset is the procedure storage offset. +proc.get_procedure_storage_offset + # get procedure storage offset pointer + mul.2 exec.memory::get_acct_procedures_section_offset add add.1 + # => [storage_offset_ptr] + + # load procedure storage offset from memory + mem_load + # => [storage_offset] +end diff --git a/miden-lib/asm/kernels/transaction/lib/prologue.masm b/miden-lib/asm/kernels/transaction/lib/prologue.masm index 1921f3ae2..2d81338f3 100644 --- a/miden-lib/asm/kernels/transaction/lib/prologue.masm +++ b/miden-lib/asm/kernels/transaction/lib/prologue.masm @@ -611,6 +611,9 @@ proc.process_account_data assert.err=ERR_PROLOGUE_OLD_ACCT_NONCE_ZERO # => [] end + + # validate account procedure storage offsets + exec.account::validate_storage_offsets end # INPUT NOTES DATA diff --git a/miden-lib/asm/miden/contracts/auth/basic.masm b/miden-lib/asm/miden/contracts/auth/basic.masm index cd352a07b..337f238c0 100644 --- a/miden-lib/asm/miden/contracts/auth/basic.masm +++ b/miden-lib/asm/miden/contracts/auth/basic.masm @@ -6,7 +6,7 @@ use.std::crypto::dsa::rpo_falcon512 # ================================================================================================= # Slot in account storage at which the public key is stored. -const.PUBLIC_KEY_SLOT=3 +const.PUBLIC_KEY_SLOT=0 #! Authenticate a transaction using the Falcon signature scheme #! Stack: [] diff --git a/miden-lib/src/accounts/faucets/mod.rs b/miden-lib/src/accounts/faucets/mod.rs index 8dbe483eb..79e932e98 100644 --- a/miden-lib/src/accounts/faucets/mod.rs +++ b/miden-lib/src/accounts/faucets/mod.rs @@ -54,7 +54,7 @@ pub fn create_basic_fungible_faucet( ); let assembler = TransactionKernel::assembler(); - let account_code = AccountCode::compile(source_code, assembler)?; + let account_code = AccountCode::compile(source_code, assembler, true)?; // First check that the metadata is valid. if decimals > MAX_DECIMALS { @@ -76,14 +76,12 @@ pub fn create_basic_fungible_faucet( // We store the authentication data and the token metadata in the account storage: // - slot 0: reserved faucet data - // - slot 1: token metadata as [max_supply, decimals, token_symbol, 0] - // - slot 2: any - // - slot 3: authentication data (must be at location 3 in storage) + // - slot 1: authentication data + // - slot 2: token metadata as [max_supply, decimals, token_symbol, 0] let account_storage = AccountStorage::new(vec![ StorageSlot::Value(reserved_data), - StorageSlot::Value(metadata), - StorageSlot::Value([ZERO, ZERO, ZERO, ZERO]), StorageSlot::Value(auth_data), + StorageSlot::Value(metadata), ])?; let account_seed = AccountId::get_account_seed( @@ -137,7 +135,7 @@ mod tests { // check that max_supply (slot 1) is 123 assert_eq!( - faucet_account.storage().get_item(1).unwrap(), + faucet_account.storage().get_item(2).unwrap(), [Felt::new(123), Felt::new(2), token_symbol.into(), ZERO].into() ); diff --git a/miden-lib/src/accounts/wallets/mod.rs b/miden-lib/src/accounts/wallets/mod.rs index 94d7f9e04..3dbb3a470 100644 --- a/miden-lib/src/accounts/wallets/mod.rs +++ b/miden-lib/src/accounts/wallets/mod.rs @@ -49,7 +49,7 @@ pub fn create_basic_wallet( ); let assembler = TransactionKernel::assembler(); - let account_code = AccountCode::compile(source_code, assembler)?; + let account_code = AccountCode::compile(source_code, assembler, false)?; let account_storage = AccountStorage::new(vec![StorageSlot::Value(storage_slot_0_data)])?; diff --git a/miden-lib/src/transaction/procedures.rs b/miden-lib/src/transaction/procedures.rs index 7920d8c6a..0761e5f67 100644 --- a/miden-lib/src/transaction/procedures.rs +++ b/miden-lib/src/transaction/procedures.rs @@ -51,10 +51,10 @@ impl TransactionKernel { const KERNEL0_PROCEDURES: [Digest; 28] = [ // account_vault_add_asset digest!( - 13754796634742484241, - 14933253614889337195, - 9946093774485040433, - 15855074050643735102 + 117074302502728688, + 11439878644778514598, + 16324818132154524894, + 6489512630979919440 ), // account_vault_get_balance digest!( @@ -72,10 +72,10 @@ const KERNEL0_PROCEDURES: [Digest; 28] = [ ), // account_vault_remove_asset digest!( - 5464500977827410683, - 14552463400672048539, - 9365606703968637084, - 10293016534863330357 + 2235246958022854005, + 5794405659267712135, + 12598697568377601936, + 10963092377629893642 ), // get_account_id digest!( @@ -86,17 +86,17 @@ const KERNEL0_PROCEDURES: [Digest; 28] = [ ), // get_account_item digest!( - 8798849230136233043, - 11890981446468028598, - 13731559665317812731, - 9455479331311731065 + 18206004789224066622, + 4233449336812475978, + 6804658891075571436, + 3940070286581972689 ), // get_account_map_item digest!( - 6867451934949452486, - 16758757541640758212, - 5960992320450449445, - 15069927195813338906 + 9209967448327341770, + 8988024763842561887, + 12632818454415758249, + 8233400257714804605 ), // get_account_nonce digest!( @@ -128,38 +128,38 @@ const KERNEL0_PROCEDURES: [Digest; 28] = [ ), // incr_account_nonce digest!( - 6704696698196624969, - 7972018098779608858, - 15521904917301964082, - 17728252908982481278 + 14589349829020905629, + 1412999498410091194, + 17301618149076423693, + 2638573156781761162 ), // set_account_code digest!( - 7692702974635116380, - 9958104701689074009, - 11194459519416984314, - 17997349034627304406 + 13397042012380537032, + 174474080566637302, + 1465955330516409718, + 13427241200626333441 ), // set_account_item digest!( - 12381295139641115436, - 4841311975278006349, - 9144015559690641835, - 7937703732911489136 + 7028525769329264650, + 7531398982722010851, + 3695061772051382659, + 2998651828779176432 ), // set_account_map_item digest!( - 15275845844959595699, - 8401120619221771250, - 7842486596353586687, - 10570005784614060544 + 7037030220885902605, + 1540995878644451898, + 11995286967942035929, + 11976243733826929886 ), // burn_asset digest!( - 11923916733603981680, - 15453826335515837585, - 203713766947597005, - 6185385630050606916 + 10812504956203964835, + 17035791932747451701, + 8886876315554082935, + 6015659628759368174 ), // get_fungible_faucet_total_issuance digest!( @@ -170,24 +170,24 @@ const KERNEL0_PROCEDURES: [Digest; 28] = [ ), // mint_asset digest!( - 4874859621272798714, - 18061047046357753110, - 726303738960380820, - 12111093375391809571 + 17329749049914215544, + 5633414059905366308, + 2519432440213570275, + 8693308573092701498 ), // add_asset_to_note digest!( - 11984752718275100784, - 11964478732054350045, - 16861290230299818441, - 13501890081578146138 + 16660224074633768406, + 3681728837439485251, + 11007804027515511275, + 7127888127578457912 ), // create_note digest!( - 16998803640885701054, - 14604787203447637278, - 13780037225056353614, - 8543984327344860200 + 386212833718199205, + 11471520476317876635, + 15232296418503481248, + 574740517948464248 ), // get_input_notes_commitment digest!( diff --git a/miden-tx/src/errors/tx_kernel_errors.rs b/miden-tx/src/errors/tx_kernel_errors.rs index 52d8f81e4..87144a9b6 100644 --- a/miden-tx/src/errors/tx_kernel_errors.rs +++ b/miden-tx/src/errors/tx_kernel_errors.rs @@ -80,13 +80,13 @@ pub const ERR_PROC_INDEX_OUT_OF_BOUNDS: u32 = 131147; pub const ERR_ACCT_CODE_HASH_MISMATCH: u32 = 131148; pub const ERR_ACCT_TOO_MANY_PROCEDURES: u32 = 131149; pub const ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS: u32 = 131150; - +pub const ERR_INVALID_FAUCET_STORAGE_OFFSET: u32 = 131151; pub const ERR_NOTE_FUNGIBLE_MAX_AMOUNT_EXCEEDED: u32 = 131152; pub const ERR_NON_FUNGIBLE_ASSET_ALREADY_EXISTS: u32 = 131153; pub const ERR_INVALID_NOTE_IDX: u32 = 131154; pub const ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS: u32 = 131155; -pub const KERNEL_ERRORS: [(u32, &str); 83] = [ +pub const KERNEL_ERRORS: [(u32, &str); 84] = [ (ERR_FAUCET_RESERVED_DATA_SLOT, "For faucets, storage slot 254 is reserved and can not be used with set_account_item procedure"), (ERR_ACCT_MUST_BE_A_FAUCET, "Procedure can only be called from faucet accounts"), (ERR_P2ID_WRONG_NUMBER_OF_INPUTS, "P2ID scripts expect exactly 1 note input"), @@ -169,5 +169,6 @@ pub const KERNEL_ERRORS: [(u32, &str); 83] = [ (ERR_ACCT_CODE_HASH_MISMATCH, "Provided account hash does not match stored account hash"), (ERR_ACCT_TOO_MANY_PROCEDURES, "Number of account procedures exceeded the maximum limit of 256"), (ERR_STORAGE_SLOT_INDEX_OUT_OF_BOUNDS, "Provided storage slot index is out of bounds"), - (ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS, "Provided kernel procedure offset is out of bounds"), + (ERR_INVALID_FAUCET_STORAGE_OFFSET, "Storage offset is invalid for a faucet account (0 is prohibited being the reserved faucet data slot)"), + (ERR_KERNEL_PROCEDURE_OFFSET_OUT_OF_BOUNDS, "Provided kernel procedure offset is out of bounds") ]; diff --git a/miden-tx/src/tests/kernel_tests/test_account.rs b/miden-tx/src/tests/kernel_tests/test_account.rs index 93c67ee27..59526a54b 100644 --- a/miden-tx/src/tests/kernel_tests/test_account.rs +++ b/miden-tx/src/tests/kernel_tests/test_account.rs @@ -10,7 +10,7 @@ use miden_objects::{ ACCOUNT_ID_REGULAR_ACCOUNT_IMMUTABLE_CODE_ON_CHAIN, ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_OFF_CHAIN, }, - AccountCode, AccountId, AccountProcedureInfo, AccountStorage, AccountType, + AccountCode, AccountId, AccountProcedureInfo, AccountStorage, AccountType, StorageSlot, }, testing::{account::AccountBuilder, prepare_word, storage::STORAGE_LEAVES_2}, transaction::TransactionScript, @@ -471,7 +471,7 @@ fn test_storage_offset() { end "; // Setup account - let code = AccountCode::compile(source_code, assembler.clone()).unwrap(); + let code = AccountCode::compile(source_code, assembler.clone(), false).unwrap(); // modify procedure offsets // TODO: We manually set the offsets here because we do not have the ability to set the @@ -484,9 +484,16 @@ fn test_storage_offset() { ]; let code = AccountCode::from_parts(code.mast().clone(), procedures_with_offsets.clone()); + let storage_slots = vec![ + StorageSlot::Value(Word::default()), + StorageSlot::Value(Word::default()), + StorageSlot::Value(Word::default()), + ]; + let (mut account, _) = AccountBuilder::new(ChaCha20Rng::from_entropy()) .code(code) .nonce(ONE) + .add_storage_slots(storage_slots) .build() .unwrap(); @@ -560,7 +567,7 @@ fn test_get_vault_commitment() { #[test] fn test_authenticate_procedure() { - let account_code = AccountCode::mock_wallet(TransactionKernel::assembler()); + let account_code = AccountCode::mock_account_code(TransactionKernel::assembler(), false); let tc_0: [Felt; 4] = account_code.procedures()[0].mast_root().as_elements().try_into().unwrap(); diff --git a/miden-tx/src/tests/kernel_tests/test_faucet.rs b/miden-tx/src/tests/kernel_tests/test_faucet.rs index 13a890ea2..ee2342fe5 100644 --- a/miden-tx/src/tests/kernel_tests/test_faucet.rs +++ b/miden-tx/src/tests/kernel_tests/test_faucet.rs @@ -1,3 +1,4 @@ +use miden_lib::transaction::memory::ACCT_STORAGE_SLOTS_SECTION_OFFSET; use miden_objects::{ accounts::account_id::testing::{ ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_1, @@ -14,7 +15,7 @@ use miden_objects::{ }, FieldElement, }; -use vm_processor::Felt; +use vm_processor::{Felt, ProcessState}; use super::ONE; use crate::{ @@ -62,18 +63,21 @@ fn test_mint_fungible_asset_succeeds() { push.{ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN} exec.asset_vault::get_balance push.{FUNGIBLE_ASSET_AMOUNT} assert_eq - - # assert the faucet storage has been updated - push.{FAUCET_STORAGE_DATA_SLOT} - call.account::get_item - push.{expected_final_storage_amount} - assert_eq end - ", - expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE + FUNGIBLE_ASSET_AMOUNT + " ); - tx_context.execute_code(&code).unwrap(); + let process = tx_context.execute_code(&code).unwrap(); + + let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE + FUNGIBLE_ASSET_AMOUNT; + let faucet_reserved_slot_storage_location = + FAUCET_STORAGE_DATA_SLOT as u32 + ACCT_STORAGE_SLOTS_SECTION_OFFSET; + + let faucet_memory_value_word = process + .get_mem_value(process.ctx(), faucet_reserved_slot_storage_location) + .unwrap(); + + assert_eq!(faucet_memory_value_word[3].as_int(), expected_final_storage_amount); } #[test] @@ -325,19 +329,22 @@ fn test_burn_fungible_asset_succeeds() { exec.asset_vault::get_balance push.{final_input_vault_asset_amount} assert_eq - - # assert the faucet storage has been updated - push.{FAUCET_STORAGE_DATA_SLOT} - call.account::get_item - push.{expected_final_storage_amount} - assert_eq end ", final_input_vault_asset_amount = CONSUMED_ASSET_1_AMOUNT - FUNGIBLE_ASSET_AMOUNT, - expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE - FUNGIBLE_ASSET_AMOUNT ); - tx_context.execute_code(&code).unwrap(); + let process = tx_context.execute_code(&code).unwrap(); + + let expected_final_storage_amount = FUNGIBLE_FAUCET_INITIAL_BALANCE - FUNGIBLE_ASSET_AMOUNT; + let faucet_reserved_slot_storage_location = + FAUCET_STORAGE_DATA_SLOT as u32 + ACCT_STORAGE_SLOTS_SECTION_OFFSET; + + let faucet_memory_value_word = process + .get_mem_value(process.ctx(), faucet_reserved_slot_storage_location) + .unwrap(); + + assert_eq!(faucet_memory_value_word[3].as_int(), expected_final_storage_amount); } #[test] diff --git a/miden-tx/src/tests/kernel_tests/test_prologue.rs b/miden-tx/src/tests/kernel_tests/test_prologue.rs index 94f303733..1e71e5e1e 100644 --- a/miden-tx/src/tests/kernel_tests/test_prologue.rs +++ b/miden-tx/src/tests/kernel_tests/test_prologue.rs @@ -385,7 +385,7 @@ fn input_notes_memory_assertions( #[cfg_attr(not(feature = "testing"), ignore)] #[test] pub fn test_prologue_create_account() { - let (account, seed) = AccountBuilder::new(ChaCha20Rng::from_entropy()) + let (account, seed) = AccountBuilder::with_mock_storage(ChaCha20Rng::from_entropy()) .default_code(TransactionKernel::testing_assembler()) .build() .unwrap(); diff --git a/miden-tx/src/tests/mod.rs b/miden-tx/src/tests/mod.rs index fc4882ea2..a47460ca5 100644 --- a/miden-tx/src/tests/mod.rs +++ b/miden-tx/src/tests/mod.rs @@ -115,7 +115,8 @@ fn executed_transaction_account_delta() { end "; let new_acct_code = - AccountCode::compile(new_acct_code_src, TransactionKernel::testing_assembler()).unwrap(); + AccountCode::compile(new_acct_code_src, TransactionKernel::testing_assembler(), false) + .unwrap(); // updated storage let updated_slot_value = [Felt::new(7), Felt::new(9), Felt::new(11), Felt::new(13)]; diff --git a/miden-tx/tests/integration/main.rs b/miden-tx/tests/integration/main.rs index 5f73cde63..70c2580d9 100644 --- a/miden-tx/tests/integration/main.rs +++ b/miden-tx/tests/integration/main.rs @@ -80,12 +80,10 @@ pub fn get_account_with_default_account_code( let account_code_src = DEFAULT_ACCOUNT_CODE; let assembler = TransactionKernel::assembler().with_debug_mode(true); - let account_code = AccountCode::compile(account_code_src, assembler).unwrap(); + let account_code = AccountCode::compile(account_code_src, assembler, false).unwrap(); let account_storage = AccountStorage::new(vec![ - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), StorageSlot::Value(public_key), + StorageSlot::Value(Word::default()), StorageSlot::Map(StorageMap::default()), ]) .unwrap(); diff --git a/miden-tx/tests/integration/scripts/faucet.rs b/miden-tx/tests/integration/scripts/faucet.rs index 9f59143a2..4147c2dc1 100644 --- a/miden-tx/tests/integration/scripts/faucet.rs +++ b/miden-tx/tests/integration/scripts/faucet.rs @@ -169,16 +169,18 @@ fn prove_faucet_contract_burn_fungible_asset_succeeds() { let fungible_asset = FungibleAsset::new(faucet_account.id(), 100).unwrap(); - // check that max_supply (slot 1) is 200 and amount already issued (slot 0) is 100 - assert_eq!( - faucet_account.storage().get_item(1).unwrap(), - [Felt::new(200), Felt::new(0), Felt::new(0), Felt::new(0)].into() - ); + // check that the faucet reserved slot has been correctly initialised assert_eq!( faucet_account.storage().get_item(FAUCET_STORAGE_DATA_SLOT).unwrap(), [Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(100)].into() ); + // check that max_supply (slot 2) is 200 and amount already issued (slot 0) is 100 + assert_eq!( + faucet_account.storage().get_item(2).unwrap(), + [Felt::new(200), Felt::new(0), Felt::new(0), Felt::new(0)].into() + ); + // need to create a note with the fungible asset to be burned let note_script = " # burn the asset @@ -237,22 +239,26 @@ fn get_faucet_account_with_max_supply_and_total_issuance( let faucet_account_id = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_OFF_CHAIN).unwrap(); let assembler = TransactionKernel::assembler(); - let faucet_account_code = AccountCode::compile(FUNGIBLE_FAUCET_SOURCE, assembler).unwrap(); + let faucet_account_code = + AccountCode::compile(FUNGIBLE_FAUCET_SOURCE, assembler, true).unwrap(); + // faucet reserved slot let faucet_storage_slot_0 = match total_issuance { Some(issuance) => StorageSlot::Value([ZERO, ZERO, ZERO, Felt::new(issuance)]), None => StorageSlot::Value(Word::default()), }; - let faucet_storage_slot_1 = - StorageSlot::Value([Felt::new(max_supply), Felt::new(0), Felt::new(0), Felt::new(0)]); + + // faucet pub_key + let faucet_storage_slot_1 = StorageSlot::Value(public_key); + + // faucet metadata let faucet_storage_slot_2 = - StorageSlot::Value([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(0)]); - let faucet_storage_slot_3 = StorageSlot::Value(public_key); + StorageSlot::Value([Felt::new(max_supply), Felt::new(0), Felt::new(0), Felt::new(0)]); + let faucet_account_storage = AccountStorage::new(vec![ faucet_storage_slot_0, faucet_storage_slot_1, faucet_storage_slot_2, - faucet_storage_slot_3, ]) .unwrap(); diff --git a/miden-tx/tests/integration/wallet/mod.rs b/miden-tx/tests/integration/wallet/mod.rs index f36583234..f35772d4d 100644 --- a/miden-tx/tests/integration/wallet/mod.rs +++ b/miden-tx/tests/integration/wallet/mod.rs @@ -82,10 +82,8 @@ fn prove_receive_asset_via_wallet() { // clone account info let account_storage = AccountStorage::new(vec![ - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), StorageSlot::Value(target_pub_key), + StorageSlot::Value(Word::default()), StorageSlot::Map(StorageMap::default()), ]) .unwrap(); @@ -166,10 +164,8 @@ fn prove_send_asset_via_wallet() { // clones account info let sender_account_storage = AccountStorage::new(vec![ - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), - StorageSlot::Value(Word::default()), StorageSlot::Value(sender_pub_key), + StorageSlot::Value(Word::default()), StorageSlot::Map(StorageMap::default()), ]) .unwrap(); diff --git a/objects/Cargo.toml b/objects/Cargo.toml index cc26850a2..f3cb2ef82 100644 --- a/objects/Cargo.toml +++ b/objects/Cargo.toml @@ -22,7 +22,6 @@ bench = false [features] concurrent = ["std"] default = ["std"] -serde = ["dep:serde", "miden-crypto/serde"] std = ["assembly/std", "miden-crypto/std", "miden-verifier/std", "vm-core/std", "vm-processor/std"] testing = ["dep:winter-rand-utils", "dep:rand"] @@ -32,7 +31,6 @@ log = { version = "0.4", optional = true } miden-crypto = { workspace = true } miden-verifier = { workspace = true } rand = { workspace = true, optional = true } -serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } vm-core = { workspace = true } vm-processor = { workspace = true } winter-rand-utils = { version = "0.9", optional = true } diff --git a/objects/README.md b/objects/README.md index e1ac52e0c..f58dedb64 100644 --- a/objects/README.md +++ b/objects/README.md @@ -44,7 +44,6 @@ Description of this crate's feature: | ------------ | --------------------------------------------------------------------------------------------- | | `std` | Enable usage of Rust's `std`, use `--no-default-features` for `no-std` support. | | `concurrent` | Enables concurrent code to speed up runtime execution. | -| `serde` | Enables serialization of most objects via `serde`. | | `testing` | Enables testing utilities and reduces proof-of-work requirements to speed up tests' runtimes. | ## License diff --git a/objects/src/accounts/account_id.rs b/objects/src/accounts/account_id.rs index e12e0b81a..f43151019 100644 --- a/objects/src/accounts/account_id.rs +++ b/objects/src/accounts/account_id.rs @@ -30,7 +30,6 @@ pub const NON_FUNGIBLE_FAUCET: u64 = 0b11; pub const REGULAR_ACCOUNT_IMMUTABLE_CODE: u64 = 0b00; pub const REGULAR_ACCOUNT_UPDATABLE_CODE: u64 = 0b01; -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u64)] pub enum AccountType { @@ -79,7 +78,6 @@ impl From for AccountType { pub const PUBLIC: u64 = 0b00; pub const PRIVATE: u64 = 0b10; -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u64)] pub enum AccountStorageMode { @@ -137,8 +135,6 @@ impl FromStr for AccountStorageMode { /// [FUNGIBLE_FAUCET], [NON_FUNGIBLE_FAUCET], [REGULAR_ACCOUNT_IMMUTABLE_CODE], and /// [REGULAR_ACCOUNT_UPDATABLE_CODE] encode the account's type. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "serde", serde(transparent))] pub struct AccountId(Felt); impl AccountId { diff --git a/objects/src/accounts/code/mod.rs b/objects/src/accounts/code/mod.rs index 3c811ce17..802f135b6 100644 --- a/objects/src/accounts/code/mod.rs +++ b/objects/src/accounts/code/mod.rs @@ -50,17 +50,24 @@ impl AccountCode { /// All procedures exported from the provided library will become members of the account's /// public interface. /// + /// # Notes + /// - A `faucet` flag has been temporarly added to this procedure enabling correct storage + /// offset of faucet procedures. This is needed because of the faucet reserved storage slot at + /// location 0. + /// /// # Errors - /// Returns an error if the number of procedures exported from the provided library is smaller - /// than 1 or greater than 256. - pub fn new(library: Library) -> Result { + /// - If the number of procedures exported from the provided library is smaller than 1 or + /// greater than 256. + pub fn new(library: Library, is_faucet: bool) -> Result { // extract procedure information from the library exports - // TODO: currently, offsets for all procedures are set to 0; instead they should be read - // from the Library metadata + // TODO: currently, offsets for all regular account procedures are set to 0 + // and offsets for faucet accounts procedures are set to 1. Instead they should + // be read from the Library metadata. let mut procedures: Vec = Vec::new(); + let storage_offset = if is_faucet { 1 } else { 0 }; for module in library.module_infos() { for proc_mast_root in module.procedure_digests() { - procedures.push(AccountProcedureInfo::new(proc_mast_root, 0)); + procedures.push(AccountProcedureInfo::new(proc_mast_root, storage_offset)); } } @@ -92,12 +99,16 @@ impl AccountCode { /// - Compilation of the provided source code fails. /// - The number of procedures exported from the provided library is smaller than 1 or greater /// than 256. - pub fn compile(source_code: impl Compile, assembler: Assembler) -> Result { + pub fn compile( + source_code: impl Compile, + assembler: Assembler, + is_faucet: bool, + ) -> Result { let library = assembler .assemble_library([source_code]) .map_err(|report| AccountError::AccountCodeAssemblyError(report.to_string()))?; - Self::new(library) + Self::new(library, is_faucet) } /// Returns a new [AccountCode] deserialized from the provided bytes. diff --git a/objects/src/accounts/header.rs b/objects/src/accounts/header.rs index b00279ff2..7662e5865 100644 --- a/objects/src/accounts/header.rs +++ b/objects/src/accounts/header.rs @@ -13,7 +13,6 @@ use super::{hash_account, Account, AccountId, Digest, Felt}; /// - storage_commitment: a commitment to the account's storage ([super::AccountStorage]). /// - code_commitment: a commitment to the account's code ([super::AccountCode]). #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct AccountHeader { id: AccountId, nonce: Felt, diff --git a/objects/src/accounts/mod.rs b/objects/src/accounts/mod.rs index c5bcfacdc..c50c5643f 100644 --- a/objects/src/accounts/mod.rs +++ b/objects/src/accounts/mod.rs @@ -26,7 +26,7 @@ mod seed; pub use seed::{get_account_seed, get_account_seed_single}; mod storage; -pub use storage::{AccountStorage, StorageMap, StorageSlot, StorageSlotType}; +pub use storage::{AccountStorage, AccountStorageHeader, StorageMap, StorageSlot, StorageSlotType}; mod header; pub use header::AccountHeader; @@ -265,24 +265,6 @@ impl Deserializable for Account { } } -#[cfg(feature = "serde")] -impl serde::Serialize for Account { - fn serialize(&self, serializer: S) -> Result { - let bytes = self.to_bytes(); - serializer.serialize_bytes(&bytes) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Account { - fn deserialize>(deserializer: D) -> Result { - use alloc::vec::Vec; - - let bytes: Vec = as serde::Deserialize>::deserialize(deserializer)?; - Self::read_from_bytes(&bytes).map_err(serde::de::Error::custom) - } -} - // HELPERS // ================================================================================================ diff --git a/objects/src/accounts/storage/header.rs b/objects/src/accounts/storage/header.rs new file mode 100644 index 000000000..27709959e --- /dev/null +++ b/objects/src/accounts/storage/header.rs @@ -0,0 +1,135 @@ +use alloc::vec::Vec; + +use vm_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + +use super::{AccountStorage, StorageSlotType, Word}; +use crate::AccountError; + +// ACCOUNT STORAGE HEADER +// ================================================================================================ + +/// Account storage header is a lighter version of the [AccountStorage] storing only the type and +/// the top-level value for each storage slot. +/// +/// That is, for complex storage slots (e.g., storage maps), the header contains only the commitment +/// to the underlying data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccountStorageHeader { + slots: Vec<(StorageSlotType, Word)>, +} + +impl AccountStorageHeader { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + + /// Returns a new instance of account storage header initialized with the provided slots. + /// + /// # Panics + /// - If the number of provided slots is greater than [AccountStorage::MAX_NUM_STORAGE_SLOTS]. + pub fn new(slots: Vec<(StorageSlotType, Word)>) -> Self { + assert!(slots.len() <= AccountStorage::MAX_NUM_STORAGE_SLOTS); + Self { slots } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns an iterator over the storage header slots. + pub fn slots(&self) -> impl Iterator { + self.slots.iter() + } + + /// Returns the number of slots contained in the storage header. + pub fn num_slots(&self) -> usize { + self.slots.len() + } + + /// Returns a slot contained in the storage header at a given index. + /// + /// # Errors + /// - If the index is out of bounds. + pub fn slot(&self, index: usize) -> Result<&(StorageSlotType, Word), AccountError> { + self.slots.get(index).ok_or(AccountError::StorageIndexOutOfBounds { + max: self.slots.len() as u8, + actual: index as u8, + }) + } +} + +impl From for AccountStorageHeader { + fn from(value: AccountStorage) -> Self { + value.get_header() + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for AccountStorageHeader { + fn write_into(&self, target: &mut W) { + let len = self.slots.len() as u8; + target.write_u8(len); + target.write_many(self.slots()) + } +} + +impl Deserializable for AccountStorageHeader { + fn read_from(source: &mut R) -> Result { + let len = source.read_u8()?; + let slots = source.read_many(len as usize)?; + // number of storage slots is guaranteed to be smaller than or equal to 255 + Ok(Self::new(slots)) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use vm_core::{ + utils::{Deserializable, Serializable}, + Felt, + }; + + use super::AccountStorageHeader; + use crate::accounts::{AccountStorage, StorageSlotType}; + + #[test] + fn test_from_account_storage() { + // create new storage header from AccountStorage + let slots = vec![ + (StorageSlotType::Value, [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]), + (StorageSlotType::Value, [Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]), + ( + StorageSlotType::Map, + [ + Felt::new(12405212884040084310), + Felt::new(17614307840949763446), + Felt::new(6101527485586301500), + Felt::new(14442045877206841081), + ], + ), + ]; + + let expected_header = AccountStorageHeader { slots }; + let account_storage = AccountStorage::mock(); + + assert_eq!(expected_header, AccountStorageHeader::from(account_storage)) + } + + #[test] + fn test_serde_account_storage_header() { + // create new storage header + let storage = AccountStorage::mock(); + let storage_header = AccountStorageHeader::from(storage); + + // serde storage header + let bytes = storage_header.to_bytes(); + let deserialized = AccountStorageHeader::read_from_bytes(&bytes).unwrap(); + + // assert deserialized == storage header + assert_eq!(storage_header, deserialized); + } +} diff --git a/objects/src/accounts/storage/map.rs b/objects/src/accounts/storage/map.rs index 4617aa462..c252cf960 100644 --- a/objects/src/accounts/storage/map.rs +++ b/objects/src/accounts/storage/map.rs @@ -23,7 +23,6 @@ pub const EMPTY_STORAGE_MAP_ROOT: Word = [ /// Account storage map is a Sparse Merkle Tree of depth 64. It can be used to store more data as /// there is in plain usage of the storage slots. The root of the SMT consumes one account storage /// slot. -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub struct StorageMap { map: Smt, diff --git a/objects/src/accounts/storage/mod.rs b/objects/src/accounts/storage/mod.rs index 7daddc247..631c97f2d 100644 --- a/objects/src/accounts/storage/mod.rs +++ b/objects/src/accounts/storage/mod.rs @@ -11,6 +11,9 @@ pub use slot::{StorageSlot, StorageSlotType}; mod map; pub use map::StorageMap; +mod header; +pub use header::AccountStorageHeader; + // ACCOUNT STORAGE // ================================================================================================ @@ -29,7 +32,7 @@ pub struct AccountStorage { } impl AccountStorage { - /// The maximum number of storage slots allowed in an [AccountStorage] + /// The maximum number of storage slots allowed in an account storage. pub const MAX_NUM_STORAGE_SLOTS: usize = 255; // CONSTRUCTOR @@ -59,7 +62,7 @@ impl AccountStorage { build_slots_commitment(&self.slots) } - /// Converts storage slots of this [AccountStorage] into a vector of field elements. + /// Converts storage slots of this account storage into a vector of field elements. /// /// This is done by first converting each procedure into exactly 8 elements as follows: /// ```text @@ -99,6 +102,13 @@ impl AccountStorage { } } + /// Returns an [AccountStorageHeader] for this account storage. + pub fn get_header(&self) -> AccountStorageHeader { + AccountStorageHeader::new( + self.slots.iter().map(|slot| (slot.slot_type(), slot.value())).collect(), + ) + } + // DATA MUTATORS // -------------------------------------------------------------------------------------------- diff --git a/objects/src/assets/fungible.rs b/objects/src/assets/fungible.rs index 5bee6ef73..ca76cb206 100644 --- a/objects/src/assets/fungible.rs +++ b/objects/src/assets/fungible.rs @@ -13,7 +13,6 @@ use super::{ /// A fungible asset consists of a faucet ID of the faucet which issued the asset as well as the /// asset amount. Asset amount is guaranteed to be 2^63 - 1 or smaller. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct FungibleAsset { faucet_id: AccountId, amount: u64, diff --git a/objects/src/assets/mod.rs b/objects/src/assets/mod.rs index bc56851b6..96594b2e8 100644 --- a/objects/src/assets/mod.rs +++ b/objects/src/assets/mod.rs @@ -64,7 +64,6 @@ pub use vault::AssetVault; /// to be different as per the faucet creation logic. Collision resistance for non-fungible assets /// issued by the same faucet is ~2^95. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Asset { Fungible(FungibleAsset), NonFungible(NonFungibleAsset), diff --git a/objects/src/assets/nonfungible.rs b/objects/src/assets/nonfungible.rs index 12aa87de2..c56a56b1a 100644 --- a/objects/src/assets/nonfungible.rs +++ b/objects/src/assets/nonfungible.rs @@ -26,8 +26,6 @@ const FAUCET_ID_POS: usize = 1; /// [NonFungibleAsset] itself does not contain the actual asset data. The container for this data /// [NonFungibleAssetDetails] struct. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "serde", serde(transparent))] pub struct NonFungibleAsset(Word); impl PartialOrd for NonFungibleAsset { @@ -204,7 +202,6 @@ impl Deserializable for NonFungibleAsset { /// /// Unlike [NonFungibleAsset] struct, this struct contains full details of a non-fungible asset. #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NonFungibleAssetDetails { faucet_id: AccountId, asset_data: Vec, diff --git a/objects/src/batches/note_tree.rs b/objects/src/batches/note_tree.rs index 39bcba6c3..21d279467 100644 --- a/objects/src/batches/note_tree.rs +++ b/objects/src/batches/note_tree.rs @@ -12,7 +12,6 @@ use crate::{ /// /// Value of each leaf is computed as: `hash(note_id || note_metadata)`. #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct BatchNoteTree(SimpleSmt); impl BatchNoteTree { diff --git a/objects/src/block/header.rs b/objects/src/block/header.rs index 41236fcf5..33525999d 100644 --- a/objects/src/block/header.rs +++ b/objects/src/block/header.rs @@ -26,7 +26,6 @@ use crate::utils::serde::{ /// - `sub_hash` is a sequential hash of all fields except the note_root. /// - `hash` is a 2-to-1 hash of the sub_hash and the note_root. #[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct BlockHeader { version: u32, prev_hash: Digest, @@ -265,7 +264,14 @@ mod tests { fn test_serde() { let chain_root: Word = rand_array(); let note_root: Word = rand_array(); - let header = BlockHeader::mock(0, Some(chain_root.into()), Some(note_root.into()), &[]); + let kernel_root: Word = rand_array(); + let header = BlockHeader::mock( + 0, + Some(chain_root.into()), + Some(note_root.into()), + &[], + kernel_root.into(), + ); let serialized = header.to_bytes(); let deserialized = BlockHeader::read_from_bytes(&serialized).unwrap(); diff --git a/objects/src/block/note_tree.rs b/objects/src/block/note_tree.rs index 737b9e79c..ab758a309 100644 --- a/objects/src/block/note_tree.rs +++ b/objects/src/block/note_tree.rs @@ -18,7 +18,6 @@ use crate::{ /// ID's leaf index is calculated as [(batch_idx * MAX_NOTES_PER_BATCH + note_idx_in_batch) * 2]. /// Metadata hash leaf is stored the next after id leaf: [id_index + 1]. #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct BlockNoteTree(SimpleSmt); impl BlockNoteTree { diff --git a/objects/src/notes/assets.rs b/objects/src/notes/assets.rs index c56f35b50..aad32f4a8 100644 --- a/objects/src/notes/assets.rs +++ b/objects/src/notes/assets.rs @@ -17,7 +17,6 @@ use crate::MAX_ASSETS_PER_NOTE; /// sequentially hashing the assets. Note that the same list of assets can result in two different /// commitments if the asset ordering is different. #[derive(Debug, Default, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteAssets { assets: Vec, hash: Digest, diff --git a/objects/src/notes/details.rs b/objects/src/notes/details.rs index b6cd22972..acae4f029 100644 --- a/objects/src/notes/details.rs +++ b/objects/src/notes/details.rs @@ -13,7 +13,6 @@ use super::{NoteAssets, NoteId, NoteInputs, NoteRecipient, NoteScript, Nullifier /// /// See [super::Note] for more details. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteDetails { assets: NoteAssets, recipient: NoteRecipient, diff --git a/objects/src/notes/execution_hint.rs b/objects/src/notes/execution_hint.rs index 973527ca4..fe9cc1ed6 100644 --- a/objects/src/notes/execution_hint.rs +++ b/objects/src/notes/execution_hint.rs @@ -19,7 +19,6 @@ const ON_BLOCK_SLOT_TAG: u8 = 3; /// This struct can be represented as the combination of a tag, and a payload. /// The tag specifies the variant of the hint, and the payload encodes the hint data. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum NoteExecutionHint { /// Unspecified note execution hint. Implies it is not knorn under which conditions the note /// is consumable. diff --git a/objects/src/notes/header.rs b/objects/src/notes/header.rs index 4cc7247a2..89b461ea2 100644 --- a/objects/src/notes/header.rs +++ b/objects/src/notes/header.rs @@ -13,7 +13,6 @@ use crate::Hasher; /// /// See [NoteId] and [NoteMetadata] for additional details. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteHeader { note_id: NoteId, note_metadata: NoteMetadata, diff --git a/objects/src/notes/location.rs b/objects/src/notes/location.rs index de2951185..cfc4b5288 100644 --- a/objects/src/notes/location.rs +++ b/objects/src/notes/location.rs @@ -5,7 +5,6 @@ use crate::{crypto::merkle::MerklePath, MAX_BATCHES_PER_BLOCK, MAX_NOTES_PER_BAT /// Contains information about the location of a note. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteLocation { /// The block number the note was created in. block_num: u32, @@ -33,7 +32,6 @@ impl NoteLocation { /// Contains the data required to prove inclusion of a note in the canonical chain. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteInclusionProof { /// Details about the note's location. location: NoteLocation, diff --git a/objects/src/notes/metadata.rs b/objects/src/notes/metadata.rs index 36c3875d2..b9299ab8f 100644 --- a/objects/src/notes/metadata.rs +++ b/objects/src/notes/metadata.rs @@ -16,7 +16,6 @@ use super::{ /// - For public notes, the second most significant bit of the tag must be 0. /// - For encrypted notes, two most significant bits of the tag must be 00. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteMetadata { /// The ID of the account which created the note. sender: AccountId, diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index 601a08aac..0e675d7f5 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -78,7 +78,6 @@ pub use file::NoteFile; /// and the kernel only verifies the source account has the assets necessary for the note creation. /// See [NoteRecipient] for more details. #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Note { header: NoteHeader, details: NoteDetails, diff --git a/objects/src/notes/note_id.rs b/objects/src/notes/note_id.rs index a37327874..d8bedd52e 100644 --- a/objects/src/notes/note_id.rs +++ b/objects/src/notes/note_id.rs @@ -25,7 +25,6 @@ use crate::utils::{ /// - To compute a note ID, we do not need to know the note's serial_num. Knowing the hash of the /// serial_num (as well as script hash, input hash, and note assets) is sufficient. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteId(Digest); impl NoteId { diff --git a/objects/src/notes/note_tag.rs b/objects/src/notes/note_tag.rs index 4df810804..3111442e7 100644 --- a/objects/src/notes/note_tag.rs +++ b/objects/src/notes/note_tag.rs @@ -26,7 +26,6 @@ const PUBLIC_USECASE: u32 = 0x80000000; /// /// The goal of the hint is to allow for a network node to quickly filter notes that are not /// intended for network execution, and skip the validation steps mentioned above. -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum NoteExecutionMode { @@ -63,7 +62,6 @@ pub enum NoteExecutionMode { /// public note for local execution is intended to allow users to search for notes that can be /// consumed right away, without requiring an off-band communication channel. #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteTag(u32); impl NoteTag { diff --git a/objects/src/notes/note_type.rs b/objects/src/notes/note_type.rs index 62cb4e845..9f914d3cb 100644 --- a/objects/src/notes/note_type.rs +++ b/objects/src/notes/note_type.rs @@ -15,7 +15,6 @@ const ENCRYPTED: u8 = 0b11; // ================================================================================================ #[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[repr(u8)] pub enum NoteType { /// Notes with this type have only their hash published to the network. diff --git a/objects/src/notes/nullifier.rs b/objects/src/notes/nullifier.rs index e6a130aca..37f4723fd 100644 --- a/objects/src/notes/nullifier.rs +++ b/objects/src/notes/nullifier.rs @@ -22,7 +22,6 @@ use crate::utils::{hex_to_bytes, HexParseError}; /// - To compute the nullifier we must know all components of the note: serial_num, script_hash, /// input_hash and asset_hash. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Nullifier(Digest); impl Nullifier { diff --git a/objects/src/notes/recipient.rs b/objects/src/notes/recipient.rs index afd3e7e51..0a5d29379 100644 --- a/objects/src/notes/recipient.rs +++ b/objects/src/notes/recipient.rs @@ -103,19 +103,3 @@ impl Deserializable for NoteRecipient { Ok(Self::new(serial_num, script, inputs)) } } - -#[cfg(feature = "serde")] -impl serde::Serialize for NoteRecipient { - fn serialize(&self, serializer: S) -> Result { - let bytes = self.to_bytes(); - serializer.serialize_bytes(&bytes) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for NoteRecipient { - fn deserialize>(deserializer: D) -> Result { - let bytes: Vec = as serde::Deserialize>::deserialize(deserializer)?; - Self::read_from_bytes(&bytes).map_err(serde::de::Error::custom) - } -} diff --git a/objects/src/testing/account.rs b/objects/src/testing/account.rs index a75a1f596..de434d5c3 100644 --- a/objects/src/testing/account.rs +++ b/objects/src/testing/account.rs @@ -47,6 +47,16 @@ impl AccountBuilder { } } + pub fn with_mock_storage(rng: T) -> Self { + Self { + assets: vec![], + storage_builder: AccountStorageBuilder::with_mock_data(), + code: None, + nonce: ZERO, + account_id_builder: AccountIdBuilder::new(rng), + } + } + pub fn add_asset(mut self, asset: Asset) -> Self { self.assets.push(asset); self @@ -76,7 +86,7 @@ impl AccountBuilder { /// Compiles [DEFAULT_ACCOUNT_CODE] into [AccountCode] and sets it. pub fn default_code(self, assembler: Assembler) -> Self { - let default_account_code = AccountCode::compile(DEFAULT_ACCOUNT_CODE, assembler) + let default_account_code = AccountCode::compile(DEFAULT_ACCOUNT_CODE, assembler, false) .expect("Default account code should compile."); self.code(default_account_code) } @@ -196,7 +206,7 @@ impl Account { AssetVault::mock() }; - let account_code = AccountCode::mock_wallet(assembler); + let account_code = AccountCode::mock_account_code(assembler, false); let account_id = AccountId::try_from(account_id).unwrap(); Account::from_parts(account_id, account_vault, account_storage, account_code, nonce) @@ -212,7 +222,7 @@ impl Account { AccountStorage::new(vec![StorageSlot::Value([ZERO, ZERO, ZERO, initial_balance])]) .unwrap(); let account_id = AccountId::try_from(account_id).unwrap(); - let account_code = AccountCode::mock_wallet(assembler); + let account_code = AccountCode::mock_account_code(assembler, true); Account::from_parts(account_id, AssetVault::default(), account_storage, account_code, nonce) } @@ -237,7 +247,7 @@ impl Account { let account_storage = AccountStorage::new(vec![StorageSlot::Map(nft_storage_map)]).unwrap(); let account_id = AccountId::try_from(account_id).unwrap(); - let account_code = AccountCode::mock_wallet(assembler); + let account_code = AccountCode::mock_account_code(assembler, true); Account::from_parts(account_id, AssetVault::default(), account_storage, account_code, nonce) } } diff --git a/objects/src/testing/account_code.rs b/objects/src/testing/account_code.rs index 1d8fc14ac..49a49a8ce 100644 --- a/objects/src/testing/account_code.rs +++ b/objects/src/testing/account_code.rs @@ -30,9 +30,9 @@ pub const DEFAULT_AUTH_SCRIPT: &str = " "; impl AccountCode { - /// Creates a mock [AccountCode] that exposes wallet interface - pub fn mock_wallet(assembler: Assembler) -> AccountCode { - AccountCode::new(Self::mock_library(assembler)).unwrap() + /// Creates a mock [AccountCode] + pub fn mock_account_code(assembler: Assembler, is_faucet: bool) -> AccountCode { + AccountCode::new(Self::mock_library(assembler), is_faucet).unwrap() } /// Creates a mock [Library] which can be used to assemble programs and as a library to create a @@ -127,11 +127,11 @@ impl AccountCode { /// Creates a mock [AccountCode] with specific code and assembler pub fn mock_with_code(source_code: &str, assembler: Assembler) -> AccountCode { - Self::compile(source_code, assembler).unwrap() + Self::compile(source_code, assembler, false).unwrap() } /// Creates a mock [AccountCode] with default assembler and mock code pub fn mock() -> AccountCode { - Self::compile(CODE, Assembler::default()).unwrap() + Self::compile(CODE, Assembler::default(), false).unwrap() } } diff --git a/objects/src/testing/account_id.rs b/objects/src/testing/account_id.rs index 677f74ff9..aca940a6d 100644 --- a/objects/src/testing/account_id.rs +++ b/objects/src/testing/account_id.rs @@ -46,7 +46,7 @@ impl AccountIdBuilder { /// Compiles [DEFAULT_ACCOUNT_CODE] into [AccountCode] and sets it. pub fn default_code(mut self, assembler: Assembler) -> Self { self.code = Some( - AccountCode::compile(DEFAULT_ACCOUNT_CODE, assembler) + AccountCode::compile(DEFAULT_ACCOUNT_CODE, assembler, false) .expect("Default account code should compile."), ); self diff --git a/objects/src/testing/block.rs b/objects/src/testing/block.rs index f8133ccf5..b54bcbe0e 100644 --- a/objects/src/testing/block.rs +++ b/objects/src/testing/block.rs @@ -20,6 +20,7 @@ impl BlockHeader { chain_root: Option, note_root: Option, accounts: &[Account], + kernel_root: Digest, ) -> Self { let acct_db = SimpleSmt::::with_leaves( accounts @@ -38,48 +39,21 @@ impl BlockHeader { let account_root = acct_db.root(); #[cfg(not(target_family = "wasm"))] - let ( - prev_hash, - chain_root, - nullifier_root, - note_root, - tx_hash, - kernel_root, - proof_hash, - timestamp, - ) = { + let (prev_hash, chain_root, nullifier_root, note_root, tx_hash, proof_hash, timestamp) = { let prev_hash = rand_array::().into(); let chain_root = chain_root.unwrap_or(rand_array::().into()); let nullifier_root = rand_array::().into(); let note_root = note_root.unwrap_or(rand_array::().into()); let tx_hash = rand_array::().into(); - let kernel_root = rand_array::().into(); let proof_hash = rand_array::().into(); let timestamp = rand_value(); - ( - prev_hash, - chain_root, - nullifier_root, - note_root, - tx_hash, - kernel_root, - proof_hash, - timestamp, - ) + (prev_hash, chain_root, nullifier_root, note_root, tx_hash, proof_hash, timestamp) }; #[cfg(target_family = "wasm")] - let ( - prev_hash, - chain_root, - nullifier_root, - note_root, - tx_hash, - kernel_root, - proof_hash, - timestamp, - ) = Default::default(); + let (prev_hash, chain_root, nullifier_root, note_root, tx_hash, proof_hash, timestamp) = + Default::default(); BlockHeader::new( 0, diff --git a/objects/src/testing/storage.rs b/objects/src/testing/storage.rs index b5886bd89..8a4365821 100644 --- a/objects/src/testing/storage.rs +++ b/objects/src/testing/storage.rs @@ -31,11 +31,15 @@ pub struct AccountStorageBuilder { /// Builder for an `AccountStorage`, the builder can be configured and used multiple times. impl AccountStorageBuilder { pub fn new() -> Self { + Self { slots: vec![] } + } + + pub fn with_mock_data() -> Self { Self { slots: vec![ - AccountStorage::mock_item_2().slot, AccountStorage::mock_item_0().slot, AccountStorage::mock_item_1().slot, + AccountStorage::mock_item_2().slot, ], } } diff --git a/objects/src/transaction/chain_mmr.rs b/objects/src/transaction/chain_mmr.rs index 048e53072..d024ef4b5 100644 --- a/objects/src/transaction/chain_mmr.rs +++ b/objects/src/transaction/chain_mmr.rs @@ -1,5 +1,7 @@ use alloc::{collections::BTreeMap, vec::Vec}; +use vm_core::utils::{Deserializable, Serializable}; + use crate::{ crypto::merkle::{InnerNodeInfo, MmrPeaks, PartialMmr}, BlockHeader, ChainMmrError, @@ -113,11 +115,37 @@ impl ChainMmr { } } +impl Serializable for ChainMmr { + fn write_into(&self, target: &mut W) { + self.mmr.write_into(target); + self.blocks.len().write_into(target); + for block in self.blocks.values() { + block.write_into(target); + } + } +} + +impl Deserializable for ChainMmr { + fn read_from( + source: &mut R, + ) -> Result { + let mmr = PartialMmr::read_from(source)?; + let block_count = usize::read_from(source)?; + let mut blocks = BTreeMap::new(); + for _ in 0..block_count { + let block = BlockHeader::read_from(source)?; + blocks.insert(block.block_num(), block); + } + Ok(Self { mmr, blocks }) + } +} // TESTS // ================================================================================================ #[cfg(test)] mod tests { + use vm_core::utils::{Deserializable, Serializable}; + use super::ChainMmr; use crate::{ alloc::vec::Vec, @@ -170,6 +198,23 @@ mod tests { ); } + #[test] + fn tst_chain_mmr_serialization() { + // create chain MMR with 3 blocks - i.e., 2 peaks + let mut mmr = Mmr::default(); + for i in 0..3 { + let block_header = int_to_block_header(i); + mmr.add(block_header.hash()); + } + let partial_mmr: PartialMmr = mmr.peaks(mmr.forest()).unwrap().into(); + let chain_mmr = ChainMmr::new(partial_mmr, Vec::new()).unwrap(); + + let bytes = chain_mmr.to_bytes(); + let deserialized = ChainMmr::read_from_bytes(&bytes).unwrap(); + + assert_eq!(chain_mmr, deserialized); + } + fn int_to_block_header(block_num: u32) -> BlockHeader { BlockHeader::new( 0, diff --git a/objects/src/transaction/inputs.rs b/objects/src/transaction/inputs.rs index e94002cb5..b5237c4fb 100644 --- a/objects/src/transaction/inputs.rs +++ b/objects/src/transaction/inputs.rs @@ -134,6 +134,28 @@ impl TransactionInputs { } } +impl Serializable for TransactionInputs { + fn write_into(&self, target: &mut W) { + self.account.write_into(target); + self.account_seed.write_into(target); + self.block_header.write_into(target); + self.block_chain.write_into(target); + self.input_notes.write_into(target); + } +} + +impl Deserializable for TransactionInputs { + fn read_from(source: &mut R) -> Result { + let account = Account::read_from(source)?; + let account_seed = source.read()?; + let block_header = BlockHeader::read_from(source)?; + let block_chain = ChainMmr::read_from(source)?; + let input_notes = InputNotes::read_from(source)?; + Self::new(account, account_seed, block_header, block_chain, input_notes) + .map_err(|err| DeserializationError::InvalidValue(format!("{}", err))) + } +} + // TO INPUT NOTE COMMITMENT // ================================================================================================ @@ -322,7 +344,6 @@ const UNAUTHENTICATED: u8 = 1; /// An input note for a transaction. #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum InputNote { /// Input notes whose existences in the chain is verified by the transaction kernel. Authenticated { note: Note, proof: NoteInclusionProof }, diff --git a/objects/src/transaction/tx_args.rs b/objects/src/transaction/tx_args.rs index 9470cde9f..3e5182a5a 100644 --- a/objects/src/transaction/tx_args.rs +++ b/objects/src/transaction/tx_args.rs @@ -27,7 +27,7 @@ use crate::{ /// different from note inputs, as the user executing the transaction can specify arbitrary note /// args. /// - Advice inputs: Provides data needed by the runtime, like the details of public output notes. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct TransactionArgs { tx_script: Option, note_args: BTreeMap, @@ -145,6 +145,24 @@ impl TransactionArgs { } } +impl Serializable for TransactionArgs { + fn write_into(&self, target: &mut W) { + self.tx_script.write_into(target); + self.note_args.write_into(target); + self.advice_inputs.write_into(target); + } +} + +impl Deserializable for TransactionArgs { + fn read_from(source: &mut R) -> Result { + let tx_script = Option::::read_from(source)?; + let note_args = BTreeMap::::read_from(source)?; + let advice_inputs = AdviceInputs::read_from(source)?; + + Ok(Self { tx_script, note_args, advice_inputs }) + } +} + // TRANSACTION SCRIPT // ================================================================================================ @@ -245,3 +263,20 @@ impl Deserializable for TransactionScript { Ok(Self::from_parts(Arc::new(mast), entrypoint, inputs)) } } + +#[cfg(test)] +mod tests { + use vm_core::utils::{Deserializable, Serializable}; + use vm_processor::AdviceMap; + + use crate::transaction::TransactionArgs; + + #[test] + fn test_tx_args_serialization() { + let args = TransactionArgs::new(None, None, AdviceMap::default()); + let bytes: std::vec::Vec = args.to_bytes(); + let decoded = TransactionArgs::read_from_bytes(&bytes).unwrap(); + + assert_eq!(args, decoded); + } +} diff --git a/objects/src/transaction/tx_witness.rs b/objects/src/transaction/tx_witness.rs index 50ba29d75..a38a921d3 100644 --- a/objects/src/transaction/tx_witness.rs +++ b/objects/src/transaction/tx_witness.rs @@ -1,3 +1,6 @@ +use vm_core::utils::{ByteReader, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{AdviceInputs, TransactionArgs, TransactionInputs}; // TRANSACTION WITNESS @@ -20,8 +23,29 @@ use super::{AdviceInputs, TransactionArgs, TransactionInputs}; /// TODO: currently, the advice witness contains redundant and irrelevant data (e.g., tx inputs /// and tx outputs). we should optimize it to contain only the minimum data required for /// executing/proving the transaction. +#[derive(Clone, Debug, PartialEq, Eq)] pub struct TransactionWitness { pub tx_inputs: TransactionInputs, pub tx_args: TransactionArgs, pub advice_witness: AdviceInputs, } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for TransactionWitness { + fn write_into(&self, target: &mut W) { + self.tx_inputs.write_into(target); + self.tx_args.write_into(target); + self.advice_witness.write_into(target); + } +} + +impl Deserializable for TransactionWitness { + fn read_from(source: &mut R) -> Result { + let tx_inputs = TransactionInputs::read_from(source)?; + let tx_args = TransactionArgs::read_from(source)?; + let advice_witness = AdviceInputs::read_from(source)?; + Ok(Self { tx_inputs, tx_args, advice_witness }) + } +}