From e959d619acaafeafad00140d59d0381f984891b6 Mon Sep 17 00:00:00 2001 From: sword_smith Date: Thu, 26 Sep 2024 13:05:47 +0200 Subject: [PATCH] collect_lock_scripts: audit preloaded witness Benchmarks and profiles reveal that this is surprisingly expensive. So we only do this check at program init. This program loops over variable-sized elements *without* any range checks, so I consider this commit essential for soundness. Cf. #190. --- benches/consensus.rs | 6 ++-- benchmarks/CollectLockScripts-2in-2out.json | 10 +++--- benchmarks/CollectLockScripts-4in-4out.json | 13 +++++++ profiles/CollectLockScripts-2in-2out.profile | 23 ++++++------ profiles/CollectLockScripts-4in-4out.profile | 14 ++++++++ .../validity/collect_lock_scripts.rs | 35 +++++++++++++------ 6 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 benchmarks/CollectLockScripts-4in-4out.json create mode 100644 profiles/CollectLockScripts-4in-4out.profile diff --git a/benches/consensus.rs b/benches/consensus.rs index 2d2ebbf6..ccdac2dc 100644 --- a/benches/consensus.rs +++ b/benches/consensus.rs @@ -148,7 +148,7 @@ mod transaction { ); } - #[divan::bench(sample_count = 1, args = [COMMON])] + #[divan::bench(sample_count = 1, args = [COMMON, LARGEISH])] fn collect_lock_scripts(args: (usize, usize)) { let (num_inputs, num_outputs) = args; let mut test_runner = TestRunner::deterministic(); @@ -166,7 +166,7 @@ mod transaction { ); } - #[divan::bench(sample_count = 1, args = [COMMON])] + #[divan::bench(sample_count = 1, args = [COMMON, LARGEISH])] fn collect_type_scripts(args: (usize, usize)) { let (num_inputs, num_outputs) = args; let mut test_runner = TestRunner::deterministic(); @@ -193,7 +193,7 @@ mod transaction { ); } - #[divan::bench(sample_count = 1, args = [COMMON])] + #[divan::bench(sample_count = 1, args = [COMMON, LARGEISH])] fn kernel_to_outputs(args: (usize, usize)) { let (num_inputs, num_outputs) = args; let mut test_runner = TestRunner::deterministic(); diff --git a/benchmarks/CollectLockScripts-2in-2out.json b/benchmarks/CollectLockScripts-2in-2out.json index ac07898f..28fda940 100644 --- a/benchmarks/CollectLockScripts-2in-2out.json +++ b/benchmarks/CollectLockScripts-2in-2out.json @@ -2,11 +2,11 @@ { "name": "CollectLockScripts-2in-2out", "benchmark_result": { - "clock_cycle_count": 306, - "hash_table_height": 211, - "u32_table_height": 102, - "op_stack_table_height": 249, - "ram_table_height": 62 + "clock_cycle_count": 606, + "hash_table_height": 391, + "u32_table_height": 200, + "op_stack_table_height": 450, + "ram_table_height": 77 }, "case": "CommonCase" } diff --git a/benchmarks/CollectLockScripts-4in-4out.json b/benchmarks/CollectLockScripts-4in-4out.json new file mode 100644 index 00000000..d6438a10 --- /dev/null +++ b/benchmarks/CollectLockScripts-4in-4out.json @@ -0,0 +1,13 @@ +[ + { + "name": "CollectLockScripts-4in-4out", + "benchmark_result": { + "clock_cycle_count": 924, + "hash_table_height": 415, + "u32_table_height": 202, + "op_stack_table_height": 686, + "ram_table_height": 143 + }, + "case": "CommonCase" + } +] \ No newline at end of file diff --git a/profiles/CollectLockScripts-2in-2out.profile b/profiles/CollectLockScripts-2in-2out.profile index 3f7db3f5..be1d2fd3 100644 --- a/profiles/CollectLockScripts-2in-2out.profile +++ b/profiles/CollectLockScripts-2in-2out.profile @@ -1,11 +1,14 @@ CollectLockScripts-2in-2out: -| Subroutine | Processor | Op Stack | RAM | Hash | U32 | -|:---------------------------------------------------------------------------------|---------------------:|---------------------:|---------------------:|---------------------:|---------------------:| -| tasmlib_hashing_algebraic_hasher_hash_varlen | 182 ( 59.5%) | 137 ( 55.0%) | 45 ( 72.6%) | 37 ( 17.5%) | 12 ( 11.8%) | -| ··tasmlib_hashing_absorb_multiple | 168 ( 54.9%) | 122 ( 49.0%) | 45 ( 72.6%) | 30 ( 14.2%) | 12 ( 11.8%) | -| ····tasmlib_hashing_absorb_multiple_hash_all_full_chunks | 30 ( 9.8%) | 20 ( 8.0%) | 40 ( 64.5%) | 24 ( 11.4%) | 0 ( 0.0%) | -| ····tasmlib_hashing_absorb_multiple_pad_varnum_zeros | 50 ( 16.3%) | 32 ( 12.9%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | -| ····tasmlib_hashing_absorb_multiple_read_remainder | 51 ( 16.7%) | 29 ( 11.6%) | 5 ( 8.1%) | 0 ( 0.0%) | 0 ( 0.0%) | -| tasmlib_hashing_eq_digest | 16 ( 5.2%) | 9 ( 3.6%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | -| neptune_consensus_transaction_collect_lock_scripts_write_all_lock_script_digests | 70 ( 22.9%) | 72 ( 28.9%) | 14 ( 22.6%) | 0 ( 0.0%) | 30 ( 29.4%) | -| Total | 306 (100.0%) | 249 (100.0%) | 62 (100.0%) | 211 (100.0%) | 102 (100.0%) | +| Subroutine | Processor | Op Stack | RAM | Hash | U32 | +|:----------------------------------------------------------------------------------|---------------------:|---------------------:|---------------------:|---------------------:|---------------------:| +| tasmlib_structure_verify_nd_si_integrity___CollectLockScriptsWitness | 297 ( 49.0%) | 194 ( 43.1%) | 15 ( 19.5%) | 0 ( 0.0%) | 188 ( 94.0%) | +| ··tasmlib_structure_tasmobject_verify_size_indicators_dyn_elem_sizes___Utxo | 236 ( 38.9%) | 156 ( 34.7%) | 12 ( 15.6%) | 0 ( 0.0%) | 120 ( 60.0%) | +| ····tasmlib_structure_tasmobject_verify_size_indicators_dyn_elem_sizes___Coin | 118 ( 19.5%) | 80 ( 17.8%) | 6 ( 7.8%) | 0 ( 0.0%) | 60 ( 30.0%) | +| tasmlib_hashing_algebraic_hasher_hash_varlen | 182 ( 30.0%) | 137 ( 30.4%) | 45 ( 58.4%) | 37 ( 9.5%) | 12 ( 6.0%) | +| ··tasmlib_hashing_absorb_multiple | 168 ( 27.7%) | 122 ( 27.1%) | 45 ( 58.4%) | 30 ( 7.7%) | 12 ( 6.0%) | +| ····tasmlib_hashing_absorb_multiple_hash_all_full_chunks | 30 ( 5.0%) | 20 ( 4.4%) | 40 ( 51.9%) | 24 ( 6.1%) | 0 ( 0.0%) | +| ····tasmlib_hashing_absorb_multiple_pad_varnum_zeros | 50 ( 8.3%) | 32 ( 7.1%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | +| ····tasmlib_hashing_absorb_multiple_read_remainder | 51 ( 8.4%) | 29 ( 6.4%) | 5 ( 6.5%) | 0 ( 0.0%) | 0 ( 0.0%) | +| tasmlib_hashing_eq_digest | 16 ( 2.6%) | 9 ( 2.0%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | +| neptune_consensus_transaction_collect_lock_scripts_write_all_lock_script_digests | 70 ( 11.6%) | 72 ( 16.0%) | 14 ( 18.2%) | 0 ( 0.0%) | 0 ( 0.0%) | +| Total | 606 (100.0%) | 450 (100.0%) | 77 (100.0%) | 391 (100.0%) | 200 (100.0%) | diff --git a/profiles/CollectLockScripts-4in-4out.profile b/profiles/CollectLockScripts-4in-4out.profile new file mode 100644 index 00000000..3f13694c --- /dev/null +++ b/profiles/CollectLockScripts-4in-4out.profile @@ -0,0 +1,14 @@ +CollectLockScripts-4in-4out: +| Subroutine | Processor | Op Stack | RAM | Hash | U32 | +|:----------------------------------------------------------------------------------|---------------------:|---------------------:|---------------------:|---------------------:|---------------------:| +| tasmlib_structure_verify_nd_si_integrity___CollectLockScriptsWitness | 527 ( 57.0%) | 346 ( 50.4%) | 27 ( 18.9%) | 0 ( 0.0%) | 189 ( 93.6%) | +| ··tasmlib_structure_tasmobject_verify_size_indicators_dyn_elem_sizes___Utxo | 466 ( 50.4%) | 308 ( 44.9%) | 24 ( 16.8%) | 0 ( 0.0%) | 120 ( 59.4%) | +| ····tasmlib_structure_tasmobject_verify_size_indicators_dyn_elem_sizes___Coin | 236 ( 25.5%) | 160 ( 23.3%) | 12 ( 8.4%) | 0 ( 0.0%) | 60 ( 29.7%) | +| tasmlib_hashing_algebraic_hasher_hash_varlen | 206 ( 22.3%) | 153 ( 22.3%) | 85 ( 59.4%) | 61 ( 14.7%) | 13 ( 6.4%) | +| ··tasmlib_hashing_absorb_multiple | 192 ( 20.8%) | 138 ( 20.1%) | 85 ( 59.4%) | 54 ( 13.0%) | 13 ( 6.4%) | +| ····tasmlib_hashing_absorb_multiple_hash_all_full_chunks | 54 ( 5.8%) | 36 ( 5.2%) | 80 ( 55.9%) | 48 ( 11.6%) | 0 ( 0.0%) | +| ····tasmlib_hashing_absorb_multiple_pad_varnum_zeros | 50 ( 5.4%) | 32 ( 4.7%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | +| ····tasmlib_hashing_absorb_multiple_read_remainder | 51 ( 5.5%) | 29 ( 4.2%) | 5 ( 3.5%) | 0 ( 0.0%) | 0 ( 0.0%) | +| tasmlib_hashing_eq_digest | 16 ( 1.7%) | 9 ( 1.3%) | 0 ( 0.0%) | 0 ( 0.0%) | 0 ( 0.0%) | +| neptune_consensus_transaction_collect_lock_scripts_write_all_lock_script_digests | 134 ( 14.5%) | 140 ( 20.4%) | 28 ( 19.6%) | 0 ( 0.0%) | 0 ( 0.0%) | +| Total | 924 (100.0%) | 686 (100.0%) | 143 (100.0%) | 415 (100.0%) | 202 (100.0%) | diff --git a/src/models/blockchain/transaction/validity/collect_lock_scripts.rs b/src/models/blockchain/transaction/validity/collect_lock_scripts.rs index a268a6bb..0a44dc97 100644 --- a/src/models/blockchain/transaction/validity/collect_lock_scripts.rs +++ b/src/models/blockchain/transaction/validity/collect_lock_scripts.rs @@ -12,6 +12,7 @@ use tasm_lib::library::Library; use tasm_lib::memory::encode_to_memory; use tasm_lib::memory::FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS; use tasm_lib::structure::tasm_object::TasmObject; +use tasm_lib::structure::verify_nd_si_integrity::VerifyNdSiIntegrity; use tasm_lib::triton_vm::prelude::*; use tasm_lib::twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use tasm_lib::Digest; @@ -96,7 +97,6 @@ impl ConsensusProgram for CollectLockScripts { fn code(&self) -> Vec { let mut library = Library::new(); - // let field_salted_input_utxos = field!(CollectLockScriptsWitness::salted_input_utxos); let field_with_size_salted_input_utxos = field_with_size!(CollectLockScriptsWitness::salted_input_utxos); let field_utxos = field!(SaltedUtxos::utxos); @@ -106,37 +106,52 @@ impl ConsensusProgram for CollectLockScripts { let write_all_lock_script_digests = "neptune_consensus_transaction_collect_lock_scripts_write_all_lock_script_digests" .to_string(); + + let audit_preloaded_data = library.import(Box::new(VerifyNdSiIntegrity::< + CollectLockScriptsWitness, + >::default())); + let payload = triton_asm! { push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS} // _ *clsw + dup 0 + call {audit_preloaded_data} + // _ *clsw witness_size + + dup 1 + // _ *clsw witness_size *clsw + {&field_with_size_salted_input_utxos} - // _ *salted_input_utxos size + // _ *clsw witness_size *salted_input_utxos size dup 1 swap 1 - // _ *salted_input_utxos *salted_input_utxos size + // _ *clsw witness_size *salted_input_utxos *salted_input_utxos size call {hash_varlen} - // _ *salted_input_utxos [salted_input_utxos_hash] + // _ *clsw witness_size *salted_input_utxos [salted_input_utxos_hash] read_io 5 - // _ *salted_input_utxos [salted_input_utxos_hash] [siud] + // _ *clsw witness_size *salted_input_utxos [salted_input_utxos_hash] [siud] call {eq_digest} assert - // _ *salted_input_utxos + // _ *clsw witness_size *salted_input_utxos {&field_utxos} - // _ *utxos_li + // _ *clsw witness_size *utxos_li read_mem 1 push 2 add - // _ N *utxos[0]_si + // _ *clsw witness_size N *utxos[0]_si push 0 swap 1 - // _ N 0 *utxos[0]_si + // _ *clsw witness_size N 0 *utxos[0]_si call {write_all_lock_script_digests} - // _ N N * + // _ *clsw witness_size N N *ptr + + pop 5 + // _ halt