From 9b35e117a3d4c70d409b857a19de3268380a4f82 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Wed, 24 Jan 2024 13:09:20 +0100 Subject: [PATCH 01/29] rename for consistency: type_script --- src/models/blockchain/transaction/validity.rs | 4 ++-- .../{kernel_to_typescripts.rs => kernel_to_type_scripts.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/models/blockchain/transaction/validity/{kernel_to_typescripts.rs => kernel_to_type_scripts.rs} (100%) diff --git a/src/models/blockchain/transaction/validity.rs b/src/models/blockchain/transaction/validity.rs index ee0f57c44..f87f944fe 100644 --- a/src/models/blockchain/transaction/validity.rs +++ b/src/models/blockchain/transaction/validity.rs @@ -1,7 +1,7 @@ use crate::prelude::{triton_vm, twenty_first}; pub mod kernel_to_lock_scripts; -pub mod kernel_to_typescripts; +pub mod kernel_to_type_scripts; pub mod lockscripts_halt; pub mod removal_records_integrity; pub mod tasm; @@ -20,7 +20,7 @@ use twenty_first::shared_math::bfield_codec::BFieldCodec; use self::lockscripts_halt::LockScriptsHalt; use self::removal_records_integrity::RemovalRecordsIntegrity; use self::{ - kernel_to_lock_scripts::KernelToLockScripts, kernel_to_typescripts::KernelToTypeScripts, + kernel_to_lock_scripts::KernelToLockScripts, kernel_to_type_scripts::KernelToTypeScripts, typescripts_halt::TypeScriptsHalt, }; use super::{transaction_kernel::TransactionKernel, PrimitiveWitness}; diff --git a/src/models/blockchain/transaction/validity/kernel_to_typescripts.rs b/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs similarity index 100% rename from src/models/blockchain/transaction/validity/kernel_to_typescripts.rs rename to src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs From 35aa955358b16e56400077b02a7a22e255410def Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Wed, 24 Jan 2024 14:25:06 +0100 Subject: [PATCH 02/29] refactor public announcements Specifically: replace public scripts (hashes) and inputs with public announcements. Closes #40. --- ...transaction_removal_records_integrity.json | 6 +- ...nsaction_transaction_kernel_mast_hash.json | 8 +- ...nsaction_removal_records_integrity.profile | 28 +++--- src/mine_loop.rs | 10 +-- src/models/blockchain/block/mod.rs | 7 +- src/models/blockchain/transaction/mod.rs | 79 ++++------------- .../blockchain/transaction/native_coin.rs | 6 +- .../transaction/transaction_kernel.rs | 42 ++++----- .../tasm/transaction_kernel_mast_hash.rs | 39 ++++---- src/models/state/archival_state.rs | 47 ++++------ src/models/state/mempool.rs | 11 +-- src/models/state/mod.rs | 39 +++----- .../wallet/address/generation_address.rs | 88 +++++++------------ src/models/state/wallet/mod.rs | 14 ++- src/rpc_server.rs | 9 +- src/tests/shared.rs | 29 +++--- 16 files changed, 168 insertions(+), 294 deletions(-) diff --git a/benchmarks/tasm_neptune_transaction_removal_records_integrity.json b/benchmarks/tasm_neptune_transaction_removal_records_integrity.json index 0ebd30a87..8ffde4dc8 100644 --- a/benchmarks/tasm_neptune_transaction_removal_records_integrity.json +++ b/benchmarks/tasm_neptune_transaction_removal_records_integrity.json @@ -1,9 +1,9 @@ [ { "name": "tasm_neptune_transaction_removal_records_integrity", - "clock_cycle_count": 29578, - "hash_table_height": 5615, - "u32_table_height": 13250, + "clock_cycle_count": 29594, + "hash_table_height": 5621, + "u32_table_height": 13245, "case": "CommonCase" } ] \ No newline at end of file diff --git a/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json b/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json index c8829fc56..85b18e806 100644 --- a/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json +++ b/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json @@ -1,14 +1,14 @@ [ { "name": "tasm_neptune_transaction_transaction_kernel_mast_hash", - "clock_cycle_count": 7822, - "hash_table_height": 3631, - "u32_table_height": 74, + "clock_cycle_count": 7754, + "hash_table_height": 3601, + "u32_table_height": 73, "case": "CommonCase" }, { "name": "tasm_neptune_transaction_transaction_kernel_mast_hash", - "clock_cycle_count": 8484, + "clock_cycle_count": 8486, "hash_table_height": 3967, "u32_table_height": 74, "case": "WorstCase" diff --git a/profiles/tasm_neptune_transaction_removal_records_integrity.profile b/profiles/tasm_neptune_transaction_removal_records_integrity.profile index 6e8a38e16..02b834559 100644 --- a/profiles/tasm_neptune_transaction_removal_records_integrity.profile +++ b/profiles/tasm_neptune_transaction_removal_records_integrity.profile @@ -1,7 +1,7 @@ tasm_neptune_transaction_removal_records_integrity: # call graph - tasm_neptune_transaction_transaction_kernel_mast_hash: 4642 + tasm_neptune_transaction_transaction_kernel_mast_hash: 4658 tasm_list_unsafeimplu32_new___digest: 36 tasm_memory_dyn_malloc: 25 tasm_list_unsafeimplu32_set_length___digest: 5 @@ -17,11 +17,11 @@ tasm_neptune_transaction_removal_records_integrity: tasm_hashing_absorb_pad_varnum_zeros: 93 tasm_hashing_absorb_read_remainder: 14 tasm_list_unsafeimplu32_set_element___digest: 8 - tasm_hashing_hash_varlen: 196 - tasm_hashing_absorb: 182 - tasm_hashing_absorb_hash_all_full_chunks: 41 - tasm_hashing_absorb_pad_varnum_zeros: 82 - tasm_hashing_absorb_read_remainder: 23 + tasm_hashing_hash_varlen: 212 + tasm_hashing_absorb: 198 + tasm_hashing_absorb_hash_all_full_chunks: 53 + tasm_hashing_absorb_pad_varnum_zeros: 104 + tasm_hashing_absorb_read_remainder: 5 tasm_list_unsafeimplu32_set_element___digest: 8 tasm_hashing_hash_varlen: 156 tasm_hashing_absorb: 142 @@ -1486,18 +1486,18 @@ tasm_neptune_transaction_removal_records_integrity: tasm_arithmetic_u64_eq: 6 tasm_list_unsafeimplu32_get_element___digest: 8 tasm_hashing_eq_digest: 15 - total: 29578 + total: 29594 # aggregated - tasm_neptune_transaction_transaction_kernel_mast_hash: 4642 + tasm_neptune_transaction_transaction_kernel_mast_hash: 4658 tasm_list_unsafeimplu32_new___digest: 144 tasm_memory_dyn_malloc: 350 tasm_list_unsafeimplu32_set_length___digest: 5 - tasm_hashing_hash_varlen: 6298 - tasm_hashing_absorb: 6088 - tasm_hashing_absorb_hash_all_full_chunks: 3987 - tasm_hashing_absorb_pad_varnum_zeros: 1153 - tasm_hashing_absorb_read_remainder: 408 + tasm_hashing_hash_varlen: 6314 + tasm_hashing_absorb: 6104 + tasm_hashing_absorb_hash_all_full_chunks: 3999 + tasm_hashing_absorb_pad_varnum_zeros: 1175 + tasm_hashing_absorb_read_remainder: 390 tasm_list_unsafeimplu32_set_element___digest: 168 tasm_list_unsafeimplu32_get_element___digest: 1480 tasm_memory_push_ram_to_stack___digest: 50 @@ -1586,4 +1586,4 @@ tasm_neptune_transaction_removal_records_integrity: tasm_arithmetic_u64_div2: 1764 tasm_hashing_swap_digest: 784 tasm_hashing_eq_digest: 30 - total: 29578 + total: 29594 diff --git a/src/mine_loop.rs b/src/mine_loop.rs index f9af1681e..d9a8855ad 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -206,7 +206,7 @@ fn make_coinbase_transaction( let kernel = TransactionKernel { inputs: vec![], outputs: vec![coinbase_addition_record], - pubscript_hashes_and_inputs: vec![], + public_announcements: vec![], fee: Amount::zero(), timestamp, coinbase: Some(coinbase_amount), @@ -220,7 +220,7 @@ fn make_coinbase_transaction( lock_script_witnesses: vec![], input_membership_proofs: vec![], output_utxos: vec![coinbase_utxo.clone()], - pubscripts: vec![], + public_announcements: vec![], mutator_set_accumulator, }; let validity_logic = @@ -494,8 +494,7 @@ mod mine_loop_tests { let four_neptune_coins = Amount::from(4).to_native_coins(); let receiver_privacy_digest = Digest::default(); let sender_randomness = Digest::default(); - let pubscript: PubScript = PubScript::default(); - let pubscript_input: Vec = vec![]; + let public_announcement = PublicAnnouncement::default(); let tx_output = Utxo { coins: four_neptune_coins, lock_script_hash: LockScript::anyone_can_spend().hash(), @@ -507,8 +506,7 @@ mod mine_loop_tests { utxo: tx_output, sender_randomness, receiver_privacy_digest, - pubscript, - pubscript_input, + public_announcement, }), ], 1.into(), diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 8f82e3b94..787b6e75f 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -116,7 +116,7 @@ impl Block { outputs: vec![], fee: 0u32.into(), timestamp, - pubscript_hashes_and_inputs: vec![], + public_announcements: vec![], coinbase: Some(total_premine_amount), mutator_set_hash: MutatorSetAccumulator::::new().hash(), }, @@ -479,7 +479,7 @@ mod block_tests { use crate::{ config_models::network::Network, models::{ - blockchain::transaction::PubScript, state::wallet::WalletSecret, + blockchain::transaction::PublicAnnouncement, state::wallet::WalletSecret, state::UtxoReceiverData, }, tests::shared::{get_mock_global_state, make_mock_block}, @@ -520,8 +520,7 @@ mod block_tests { // create a new transaction, merge it into block 1 and check that block 1 is still valid let new_utxo = Utxo::new_native_coin(other_address.lock_script(), 10.into()); let reciever_data = UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: other_address.privacy_digest, sender_randomness: random(), utxo: new_utxo, diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index 576a47da7..5b48c3f1f 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -16,8 +16,6 @@ use std::cmp::max; use std::hash::{Hash as StdHash, Hasher as StdHasher}; use std::time::SystemTime; use tracing::{debug, error, warn}; -use triton_vm::instruction::LabelledInstruction; -use triton_vm::program::Program; use triton_vm::proof::Proof; use triton_vm::{ prelude::{NonDeterminism, PublicInput}, @@ -30,7 +28,7 @@ use twenty_first::util_types::emojihash_trait::Emojihash; use self::amount::Amount; use self::native_coin::native_coin_program; -use self::transaction_kernel::{PubScriptHashAndInput, TransactionKernel}; +use self::transaction_kernel::TransactionKernel; use self::utxo::{LockScript, TypeScript, Utxo}; use self::validity::TransactionValidationLogic; use super::block::Block; @@ -41,32 +39,14 @@ use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulat use crate::util_types::mutator_set::mutator_set_trait::MutatorSet; use crate::util_types::mutator_set::removal_record::RemovalRecord; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] -pub struct PubScript { - pub program: Program, -} - -impl Default for PubScript { - fn default() -> Self { - Self { - program: Program::new(&triton_asm!(halt)), - } - } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec, Default)] +pub struct PublicAnnouncement { + pub message: Vec, } -impl From> for PubScript { - fn from(instrs: Vec) -> Self { - Self { - program: Program::new(&instrs), - } - } -} - -impl From<&[LabelledInstruction]> for PubScript { - fn from(instrs: &[LabelledInstruction]) -> Self { - Self { - program: Program::new(instrs), - } +impl PublicAnnouncement { + pub fn new(message: Vec) -> Self { + Self { message } } } @@ -80,7 +60,7 @@ pub struct PrimitiveWitness { pub lock_script_witnesses: Vec>, pub input_membership_proofs: Vec>, pub output_utxos: Vec, - pub pubscripts: Vec, + pub public_announcements: Vec, pub mutator_set_accumulator: MutatorSetAccumulator, } @@ -282,9 +262,9 @@ impl Transaction { let merged_kernel = TransactionKernel { inputs: [self.kernel.inputs, other.kernel.inputs].concat(), outputs: [self.kernel.outputs, other.kernel.outputs].concat(), - pubscript_hashes_and_inputs: [ - self.kernel.pubscript_hashes_and_inputs, - other.kernel.pubscript_hashes_and_inputs, + public_announcements: [ + self.kernel.public_announcements, + other.kernel.public_announcements, ] .concat(), fee: self.kernel.fee + other.kernel.fee, @@ -328,9 +308,9 @@ impl Transaction { other_witness.output_utxos.clone(), ] .concat(), - pubscripts: [ - self_witness.pubscripts.clone(), - other_witness.pubscripts.clone(), + public_announcements: [ + self_witness.public_announcements.clone(), + other_witness.public_announcements.clone(), ] .concat(), mutator_set_accumulator: self_witness.mutator_set_accumulator.clone(), @@ -532,34 +512,7 @@ impl Transaction { return false; } - // verify pubscripts - for ( - PubScriptHashAndInput { - pubscript_hash, - pubscript_input, - }, - pubscript, - ) in self - .kernel - .pubscript_hashes_and_inputs - .iter() - .zip(primitive_witness.pubscripts.iter()) - { - if *pubscript_hash != Hash::hash(pubscript) { - return false; - } - - let secret_input: Vec = vec![]; - - // The pubscript is satisfied if it halts gracefully without crashing. - if let Err(err) = pubscript.program.run( - PublicInput::new(pubscript_input.to_vec()), - NonDeterminism::new(secret_input), - ) { - warn!("Could not verify pubscript for transaction; got err: \"{err}\"."); - return false; - } - } + // in regards to public announcements: there isn't anything to verify true } @@ -578,7 +531,7 @@ mod witness_tests { lock_script_witnesses: vec![], input_membership_proofs: vec![], output_utxos: vec![], - pubscripts: vec![], + public_announcements: vec![], mutator_set_accumulator: MutatorSetAccumulator::new(), }; diff --git a/src/models/blockchain/transaction/native_coin.rs b/src/models/blockchain/transaction/native_coin.rs index aa37328e4..335b31511 100644 --- a/src/models/blockchain/transaction/native_coin.rs +++ b/src/models/blockchain/transaction/native_coin.rs @@ -43,7 +43,7 @@ pub fn native_coin_reference( // Kernel mast hash is the Merkle root whose leafs are // - hash_varlen(input_sequence) // - hash_varlen(output_sequence) - // - hash_varlen(pubscript_hashes_and_inputs_sequence) + // - hash_varlen(public_announcements_sequence) // - hash_varlen(fee_sequence) // - hash_varlen(coinbase_sequence) // - hash_varlen(timestamp_sequence) @@ -63,7 +63,7 @@ pub fn native_coin_reference( *Vec::>::decode(&read_secret_input)?; let input_sequence = &sequences[0]; let output_sequence = &sequences[1]; - let pubscript_sequence = &sequences[2]; + let public_announcements_sequence = &sequences[2]; let fee_sequence = &sequences[3]; let coinbase_sequence = &sequences[4]; let timestamp_sequence = &sequences[5]; @@ -116,7 +116,7 @@ pub fn native_coin_reference( let leafs = [ Hash::hash_varlen(input_sequence), Hash::hash_varlen(output_sequence), - Hash::hash_varlen(pubscript_sequence), + Hash::hash_varlen(public_announcements_sequence), Hash::hash_varlen(fee_sequence), Hash::hash_varlen(coinbase_sequence), Hash::hash_varlen(timestamp_sequence), diff --git a/src/models/blockchain/transaction/transaction_kernel.rs b/src/models/blockchain/transaction/transaction_kernel.rs index ac3571d01..12f1c294a 100644 --- a/src/models/blockchain/transaction/transaction_kernel.rs +++ b/src/models/blockchain/transaction/transaction_kernel.rs @@ -14,7 +14,7 @@ use twenty_first::{ }, }; -use super::{amount::pseudorandom_amount, Amount}; +use super::{amount::pseudorandom_amount, Amount, PublicAnnouncement}; use crate::{ util_types::mutator_set::{ addition_record::{pseudorandom_addition_record, AdditionRecord}, @@ -23,21 +23,11 @@ use crate::{ Hash, }; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] -pub struct PubScriptHashAndInput { - pub pubscript_hash: Digest, - pub pubscript_input: Vec, -} - -pub fn pseudorandom_pubscript_struct(seed: [u8; 32]) -> PubScriptHashAndInput { +pub fn pseudorandom_public_announcement(seed: [u8; 32]) -> PublicAnnouncement { let mut rng: StdRng = SeedableRng::from_seed(seed); - let digest: Digest = rng.gen(); let len = 10 + (rng.next_u32() % 50) as usize; - let input: Vec = (0..len).map(|_| rng.gen()).collect_vec(); - PubScriptHashAndInput { - pubscript_hash: digest, - pubscript_input: input, - } + let message = (0..len).map(|_| rng.gen()).collect_vec(); + PublicAnnouncement { message } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec, TasmObject)] @@ -47,7 +37,7 @@ pub struct TransactionKernel { // `outputs` contains the commitments (addition records) that go into the AOCL pub outputs: Vec, - pub pubscript_hashes_and_inputs: Vec, + pub public_announcements: Vec, pub fee: Amount, pub coinbase: Option, @@ -88,7 +78,7 @@ impl TransactionKernel { let output_utxos_sequence = self.outputs.encode(); - let pubscript_sequence = self.pubscript_hashes_and_inputs.encode(); + let pubscript_sequence = self.public_announcements.encode(); let fee_sequence = self.fee.encode(); @@ -161,7 +151,7 @@ pub fn pseudorandom_transaction_kernel( .map(|_| pseudorandom_addition_record(rng.gen::<[u8; 32]>())) .collect_vec(); let pubscripts = (0..num_pubscripts) - .map(|_| pseudorandom_pubscript_struct(rng.gen::<[u8; 32]>())) + .map(|_| pseudorandom_public_announcement(rng.gen::<[u8; 32]>())) .collect_vec(); let fee = pseudorandom_amount(rng.gen::<[u8; 32]>()); let coinbase = pseudorandom_option(rng.gen(), pseudorandom_amount(rng.gen::<[u8; 32]>())); @@ -171,7 +161,7 @@ pub fn pseudorandom_transaction_kernel( TransactionKernel { inputs, outputs, - pubscript_hashes_and_inputs: pubscripts, + public_announcements: pubscripts, fee, coinbase, timestamp, @@ -185,25 +175,25 @@ pub mod transaction_kernel_tests { use rand::{random, thread_rng, Rng, RngCore}; use crate::{ - tests::shared::{random_pubscript_struct, random_transaction_kernel}, + tests::shared::{random_public_announcement, random_transaction_kernel}, util_types::mutator_set::{removal_record::AbsoluteIndexSet, shared::NUM_TRIALS}, }; use super::*; #[test] - pub fn decode_pubscripthash_and_input() { - let pubscript = random_pubscript_struct(); + pub fn decode_public_announcement() { + let pubscript = random_public_announcement(); let encoded = pubscript.encode(); - let decoded = *PubScriptHashAndInput::decode(&encoded).unwrap(); + let decoded = *PublicAnnouncement::decode(&encoded).unwrap(); assert_eq!(pubscript, decoded); } #[test] - pub fn decode_pubscripthashes_and_inputs() { - let pubscripts = vec![random_pubscript_struct(), random_pubscript_struct()]; + pub fn decode_public_announcements() { + let pubscripts = vec![random_public_announcement(), random_public_announcement()]; let encoded = pubscripts.encode(); - let decoded = *Vec::::decode(&encoded).unwrap(); + let decoded = *Vec::::decode(&encoded).unwrap(); assert_eq!(pubscripts, decoded); } @@ -234,7 +224,7 @@ pub mod transaction_kernel_tests { outputs: vec![AdditionRecord { canonical_commitment: random(), }], - pubscript_hashes_and_inputs: Default::default(), + public_announcements: Default::default(), fee: Amount::one(), coinbase: None, timestamp: Default::default(), diff --git a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs index a65e715ff..7c43e00b2 100644 --- a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs +++ b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs @@ -97,8 +97,8 @@ impl BasicSnippet for TransactionKernelMastHash { let kernel_to_inputs_with_size = tasm_lib::field_with_size!(TransactionKernel::inputs); let kernel_to_outputs_with_size = tasm_lib::field_with_size!(TransactionKernel::outputs); - let kernel_to_pubscripts_with_size = - tasm_lib::field_with_size!(TransactionKernel::pubscript_hashes_and_inputs); + let kernel_to_public_announcements = + tasm_lib::field_with_size!(TransactionKernel::public_announcements); let kernel_to_fee_with_size = tasm_lib::field_with_size!(TransactionKernel::fee); let kernel_to_coinbase_with_size = tasm_lib::field_with_size!(TransactionKernel::coinbase); let kernel_to_timestamp_with_size = @@ -122,29 +122,29 @@ impl BasicSnippet for TransactionKernelMastHash { // populate list[8] with inputs digest dup 1 // _ *kernel *list *kernel {&kernel_to_inputs_with_size} - // _ *kernel *list *inputs *inputs_size + // _ *kernel *list *inputs inputs_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 8 // _ *kernel *list d4 d3 d2 d1 d0 *list 8 call {set_element} // _ *kernel *list // populate list[9] with outputs digest dup 1 // _ *kernel *list *kernel - {&kernel_to_outputs_with_size} // _ *kernel *list *outputs *outputs_size + {&kernel_to_outputs_with_size} // _ *kernel *list *outputs outputs_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 9 // _ *kernel *list d4 d3 d2 d1 d0 *list 9 call {set_element} // _ *kernel *list - // populate list[10] with pubscript_hashes_and_inputs digest + // populate list[10] with public_announcements digest dup 1 // _ *kernel *list *kernel - {&kernel_to_pubscripts_with_size} - // _ *kernel *list *pubscript_hashes_and_inputs *pubscript_hashes_and_inputs_size_size + {&kernel_to_public_announcements} + // _ *kernel *list *kernel_to_public_announcements kernel_to_public_announcements_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 10 // _ *kernel *list d4 d3 d2 d1 d0 *list 10 call {set_element} // _ *kernel *list // populate list[11] with fee digest dup 1 // _ *kernel *list *kernel - {&kernel_to_fee_with_size} // _ *kernel *list *fee *fee_size + {&kernel_to_fee_with_size} // _ *kernel *list *fee fee_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 11 // _ *kernel *list d4 d3 d2 d1 d0 *list 11 call {set_element} // _ *kernel *list @@ -152,7 +152,7 @@ impl BasicSnippet for TransactionKernelMastHash { // populate list[12] with coinbase digest dup 1 // _ *kernel *list *kernel {&kernel_to_coinbase_with_size} - // _ *kernel *list *coinbase *coinbase_size + // _ *kernel *list *coinbase coinbase_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 12 // _ *kernel *list d4 d3 d2 d1 d0 *list 12 call {set_element} // _ *kernel *list @@ -160,7 +160,7 @@ impl BasicSnippet for TransactionKernelMastHash { // populate list[13] with timestamp digest dup 1 // _ *kernel *list *kernel {&kernel_to_timestamp_with_size} - // _ *kernel *list *timestamp *timestamp_size + // _ *kernel *list *timestamp timestamp_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 13 // _ *kernel *list d4 d3 d2 d1 d0 *list 13 call {set_element} // _ *kernel *list @@ -168,7 +168,7 @@ impl BasicSnippet for TransactionKernelMastHash { // populate list[14] with mutator set hash digest dup 1 // _ *kernel *list *kernel {&kernel_to_mutator_set_hash_with_size} - // _ *kernel *list *mutator_set_hash *mutator_set_hash_size + // _ *kernel *list *mutator_set_hash mutator_set_hash_size call {hash_varlen} // _ *kernel *list d4 d3 d2 d1 d0 dup 5 push 14 // _ *kernel *list d4 d3 d2 d1 d0 *list 14 call {set_element} // _ *kernel *list @@ -303,21 +303,20 @@ impl Function for TransactionKernelMastHash { let outputs_hash = Hash::hash_varlen(&outputs_encoded); // address += BFieldElement::one() + BFieldElement::new(outputs_size as u64); - // pubscript_hashes_and_inputs - // let pubscript_hashes_and_inputs_size = memory.get(&address).unwrap().value() as usize; - // let pubscript_hashes_and_inputs_encoded = (0..pubscript_hashes_and_inputs_size) + // public_announcements + // let public_announcements_size = memory.get(&address).unwrap().value() as usize; + // let public_announcements_encoded = (0..public_announcements_size) // .map(|i| { // *memory // .get(&(address + BFieldElement::new(i as u64))) // .unwrap() // }) // .collect_vec(); - let pubscript_hashes_and_inputs = kernel.pubscript_hashes_and_inputs; - let pubscript_hashes_and_inputs_encoded = pubscript_hashes_and_inputs.encode(); - let pubscript_hashes_and_inputs_hash = - Hash::hash_varlen(&pubscript_hashes_and_inputs_encoded); + let public_announcements = kernel.public_announcements; + let public_announcements_encoded = public_announcements.encode(); + let public_announcements_hash = Hash::hash_varlen(&public_announcements_encoded); // address += - // BFieldElement::one() + BFieldElement::new(pubscript_hashes_and_inputs_size as u64); + // BFieldElement::one() + BFieldElement::new(public_announcements_size as u64); // fee // let fee_size = memory.get(&address).unwrap().value() as usize; @@ -383,7 +382,7 @@ impl Function for TransactionKernelMastHash { let leafs = [ inputs_hash, outputs_hash, - pubscript_hashes_and_inputs_hash, + public_announcements_hash, fee_hash, coinbase_hash, timestamp_hash, diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 405648ff3..4f247984d 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -701,7 +701,7 @@ mod archival_state_tests { use crate::config_models::network::Network; use crate::models::blockchain::transaction::utxo::LockScript; - use crate::models::blockchain::transaction::PubScript; + use crate::models::blockchain::transaction::PublicAnnouncement; use crate::models::blockchain::transaction::{amount::Amount, utxo::Utxo}; use crate::models::state::archival_state::ArchivalState; use crate::models::state::wallet::utxo_notification_pool::UtxoNotifier; @@ -866,8 +866,7 @@ mod archival_state_tests { let sender_tx = genesis_receiver_global_state .create_transaction( vec![UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: random(), sender_randomness: random(), utxo: Utxo { @@ -984,8 +983,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: random(), - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, UtxoReceiverData { utxo: Utxo { @@ -994,8 +992,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: random(), - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, ]; let sender_tx = global_state_lock @@ -1097,8 +1094,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: random(), - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, UtxoReceiverData { utxo: Utxo { @@ -1107,8 +1103,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: random(), - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, ]; let sender_tx = global_state @@ -1237,8 +1232,7 @@ mod archival_state_tests { // Add a valid input to the block transaction let one_money: Amount = Into::::into(1); let receiver_data = UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: random(), sender_randomness: random(), utxo: Utxo { @@ -1291,8 +1285,7 @@ mod archival_state_tests { let sender_randomness: Digest = random(); let receiver_data_for_alice = vec![ UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: alice_spending_key.to_address().privacy_digest, sender_randomness, utxo: Utxo { @@ -1301,8 +1294,7 @@ mod archival_state_tests { }, }, UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: alice_spending_key.to_address().privacy_digest, sender_randomness, utxo: Utxo { @@ -1314,8 +1306,7 @@ mod archival_state_tests { // Two outputs for Bob let receiver_data_for_bob = vec![ UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: bob_spending_key.to_address().privacy_digest, sender_randomness, utxo: Utxo { @@ -1324,8 +1315,7 @@ mod archival_state_tests { }, }, UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: bob_spending_key.to_address().privacy_digest, sender_randomness, utxo: Utxo { @@ -1466,8 +1456,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: genesis_spending_key.to_address().privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, UtxoReceiverData { utxo: Utxo { @@ -1476,8 +1465,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: genesis_spending_key.to_address().privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, ]; let tx_from_alice = alice_state_lock @@ -1494,8 +1482,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: genesis_spending_key.to_address().privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, UtxoReceiverData { utxo: Utxo { @@ -1504,8 +1491,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: genesis_spending_key.to_address().privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, UtxoReceiverData { utxo: Utxo { @@ -1514,8 +1500,7 @@ mod archival_state_tests { }, sender_randomness: random(), receiver_privacy_digest: genesis_spending_key.to_address().privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }, ]; let tx_from_bob = bob_state_lock diff --git a/src/models/state/mempool.rs b/src/models/state/mempool.rs index eafa31db4..d325879a6 100644 --- a/src/models/state/mempool.rs +++ b/src/models/state/mempool.rs @@ -384,7 +384,7 @@ mod tests { models::{ blockchain::{ block::block_height::BlockHeight, - transaction::{amount::Amount, utxo::Utxo, PubScript, Transaction}, + transaction::{amount::Amount, utxo::Utxo, PublicAnnouncement, Transaction}, }, shared::SIZE_20MB_IN_BYTES, state::{ @@ -582,8 +582,7 @@ mod tests { }; output_utxos_generated_by_me.push(UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: premine_receiver_address.privacy_digest, sender_randomness: random(), utxo: new_utxo, @@ -610,8 +609,7 @@ mod tests { }, sender_randomness: random(), receiver_privacy_digest: other_receiver_address.privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }]; let tx_by_other_original = other_global_state .create_transaction(output_utxo_data_by_miner, 1.into()) @@ -712,8 +710,7 @@ mod tests { utxo, receiver_privacy_digest: premine_address.privacy_digest, sender_randomness: random(), - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }; let tx_by_preminer_low_fee = preminer_state .create_transaction(vec![receiver_data.clone()], 1.into()) diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index b48c8652f..e519013ac 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -24,16 +24,14 @@ use self::wallet::wallet_state::WalletState; use self::wallet::wallet_status::WalletStatus; use super::blockchain::block::block_height::BlockHeight; use super::blockchain::block::Block; -use super::blockchain::transaction::transaction_kernel::{ - PubScriptHashAndInput, TransactionKernel, -}; +use super::blockchain::transaction::transaction_kernel::TransactionKernel; use super::blockchain::transaction::utxo::{LockScript, TypeScript, Utxo}; use super::blockchain::transaction::validity::{TransactionValidationLogic, ValidationLogic}; use super::blockchain::transaction::{ amount::{Amount, Sign}, Transaction, }; -use super::blockchain::transaction::{PrimitiveWitness, PubScript, Witness}; +use super::blockchain::transaction::{PrimitiveWitness, PublicAnnouncement, Witness}; use crate::config_models::cli_args; use crate::models::peer::HandshakeData; use crate::models::state::wallet::monitored_utxo::MonitoredUtxo; @@ -228,8 +226,7 @@ pub struct UtxoReceiverData { pub utxo: Utxo, pub sender_randomness: Digest, pub receiver_privacy_digest: Digest, - pub pubscript: PubScript, - pub pubscript_input: Vec, + pub public_announcement: PublicAnnouncement, } impl GlobalState { @@ -468,12 +465,9 @@ impl GlobalState { .expect("Adding change UTXO to UTXO notification pool must succeed"); } - let pubscript_hashes_and_inputs = receiver_data + let public_announcements = receiver_data .iter() - .map(|x| PubScriptHashAndInput { - pubscript_hash: Hash::hash(&x.pubscript), - pubscript_input: x.pubscript_input.clone(), - }) + .map(|x| x.public_announcement.clone()) .collect_vec(); let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -483,7 +477,7 @@ impl GlobalState { let kernel = TransactionKernel { inputs, outputs: transaction_outputs, - pubscript_hashes_and_inputs, + public_announcements: public_announcements.clone(), fee, timestamp: BFieldElement::new(timestamp.try_into().unwrap()), coinbase: None, @@ -532,11 +526,6 @@ impl GlobalState { ); } - let pubscripts = receiver_data - .iter() - .map(|rd| rd.pubscript.clone()) - .collect_vec(); - let mutator_set_accumulator = self .chain .light_state() @@ -556,7 +545,7 @@ impl GlobalState { lock_script_witnesses: vec![secret_input; spendable_utxos_and_mps.len()], input_membership_proofs, output_utxos: output_utxos.clone(), - pubscripts, + public_announcements, mutator_set_accumulator, }; @@ -1122,15 +1111,14 @@ mod global_state_tests { }; let sender_randomness = Digest::default(); let receiver_privacy_digest = recipient_address.privacy_digest; - let (pubscript, pubscript_input) = recipient_address - .generate_pubscript_and_input(&output_utxo, sender_randomness) + let public_announcement = recipient_address + .generate_public_announcement(&output_utxo, sender_randomness) .unwrap(); let receiver_data = vec![UtxoReceiverData { utxo: output_utxo.clone(), sender_randomness, receiver_privacy_digest, - pubscript, - pubscript_input, + public_announcement, }]; let tx: Transaction = global_state_lock .lock_guard_mut() @@ -1165,16 +1153,15 @@ mod global_state_tests { }; let other_sender_randomness = Digest::default(); let other_receiver_digest = receiving_address.privacy_digest; - let (other_pubscript, other_pubscript_input) = receiving_address - .generate_pubscript_and_input(&utxo, other_sender_randomness) + let other_public_announcement = receiving_address + .generate_public_announcement(&utxo, other_sender_randomness) .unwrap(); output_utxos.push(utxo.clone()); other_receiver_data.push(UtxoReceiverData { utxo, sender_randomness: other_sender_randomness, receiver_privacy_digest: other_receiver_digest, - pubscript: other_pubscript, - pubscript_input: other_pubscript_input, + public_announcement: other_public_announcement, }); } diff --git a/src/models/state/wallet/address/generation_address.rs b/src/models/state/wallet/address/generation_address.rs index b29c373b7..5d6317278 100644 --- a/src/models/state/wallet/address/generation_address.rs +++ b/src/models/state/wallet/address/generation_address.rs @@ -26,7 +26,7 @@ use crate::config_models::network::Network; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::utxo::LockScript; use crate::models::blockchain::transaction::utxo::Utxo; -use crate::models::blockchain::transaction::PubScript; +use crate::models::blockchain::transaction::PublicAnnouncement; use crate::models::blockchain::transaction::Transaction; use crate::util_types::mutator_set::addition_record::AdditionRecord; use crate::util_types::mutator_set::mutator_set_trait::commit; @@ -50,30 +50,32 @@ pub struct ReceivingAddress { pub spending_lock: Digest, } -fn pubscript_input_is_marked(pubscript_input: &[BFieldElement]) -> bool { - matches!(pubscript_input.first(), Some(&GENERATION_FLAG)) +/// Determine if the public announcement is flagged to indicate it might be a generation +/// address ciphertext. +fn public_announcement_is_marked(announcement: &PublicAnnouncement) -> bool { + matches!(announcement.message.first(), Some(&GENERATION_FLAG)) } fn derive_receiver_id(seed: Digest) -> BFieldElement { Hash::hash_varlen(&[seed.values().to_vec(), vec![BFieldElement::new(2)]].concat()).values()[0] } -fn receiver_identifier_from_pubscript_input( - public_script_input: &[BFieldElement], +fn receiver_identifier_from_public_announcement( + announcement: &PublicAnnouncement, ) -> Result { - match public_script_input.get(1) { + match announcement.message.get(1) { Some(id) => Ok(*id), - None => bail!("Public script does not contain receiver ID"), + None => bail!("Public announcement does not contain receiver ID"), } } -fn ciphertext_from_pubscript_input( - pubscript_input: &[BFieldElement], +fn ciphertext_from_public_announcement( + announcement: &PublicAnnouncement, ) -> Result> { - if pubscript_input.len() <= 2 { - bail!("Public script does not contain ciphertext."); + if announcement.message.len() <= 2 { + bail!("Public announcement does not contain ciphertext."); } - Ok(pubscript_input[2..].to_vec()) + Ok(announcement.message[2..].to_vec()) } /// Encodes a slice of bytes to a vec of BFieldElements. This @@ -162,13 +164,13 @@ impl SpendingKey { let mut received_utxos_with_randomnesses = vec![]; // for all public scripts that contain a ciphertext for me, - for matching_script in transaction + for matching_announcement in transaction .kernel - .pubscript_hashes_and_inputs + .public_announcements .iter() - .filter(|psd| pubscript_input_is_marked(&psd.pubscript_input)) - .filter(|psd| { - let receiver_id = receiver_identifier_from_pubscript_input(&psd.pubscript_input); + .filter(|pa| public_announcement_is_marked(pa)) + .filter(|pa| { + let receiver_id = receiver_identifier_from_public_announcement(pa); match receiver_id { Ok(recid) => recid == self.receiver_identifier, Err(_) => false, @@ -176,7 +178,7 @@ impl SpendingKey { }) { // decrypt it to obtain the utxo and sender randomness - let ciphertext = ciphertext_from_pubscript_input(&matching_script.pubscript_input); + let ciphertext = ciphertext_from_public_announcement(matching_announcement); let decryption_result = match ciphertext { Ok(ctxt) => self.decrypt(&ctxt), _ => { @@ -339,23 +341,18 @@ impl ReceivingAddress { .concat()) } - /// Generate a pubscript input, which is a ciphertext only the + /// Generate a public announcement, which is a ciphertext only the /// recipient can decrypt, along with a pubscript that reads /// some input of that length. - pub fn generate_pubscript_and_input( + pub fn generate_public_announcement( &self, utxo: &Utxo, sender_randomness: Digest, - ) -> Result<(PubScript, Vec)> { + ) -> Result { let mut ciphertext = vec![GENERATION_FLAG, self.receiver_identifier]; ciphertext.append(&mut self.encrypt(utxo, sender_randomness)?); - let pubscript = triton_asm!( - {&tasm_lib::io::InputSource::StdIn.read_words(ciphertext.len())} - halt - ); - - Ok((pubscript.into(), ciphertext)) + Ok(PublicAnnouncement::new(ciphertext)) } /// Generate a lock script from the spending lock. Satisfaction @@ -363,27 +360,6 @@ impl ReceivingAddress { /// the transaction. The logic contained in here should be /// identical to `verify_unlock`. pub fn lock_script(&self) -> LockScript { - // currently this script is just a placeholder - // const DIVINE: BFieldElement = BFieldElement::new(8); - // const HASH: BFieldElement = BFieldElement::new(48); - // const POP: BFieldElement = BFieldElement::new(2); - // const PUSH: BFieldElement = BFieldElement::new(1); - // const ASSERT_VECTOR: BFieldElement = BFieldElement::new(64); - // const READ_IO: BFieldElement = BFieldElement::new(128); - // let mut push_digest = vec![]; - // for elem in self.spending_lock.values().iter().rev() { - // push_digest.append(&mut vec![PUSH, *elem]); - // } - // let instrs = vec![ - // vec![ - // DIVINE, DIVINE, DIVINE, DIVINE, DIVINE, HASH, POP, POP, POP, POP, POP, - // ], - // push_digest, - // vec![ASSERT_VECTOR], - // vec![READ_IO, READ_IO, READ_IO, READ_IO, READ_IO], - // ] - // .concat(); - let mut push_spending_lock_digest_to_stack = vec![]; for elem in self.spending_lock.values().iter().rev() { push_spending_lock_digest_to_stack.push(triton_instr!(push elem.value())); @@ -402,6 +378,7 @@ impl ReceivingAddress { } fn get_hrp(network: Network) -> String { + // NOLGA: Neptune lattice-based generation address let mut hrp = "nolga".to_string(); let network_byte: char = match network { Network::Alpha => 'm', @@ -468,7 +445,7 @@ mod test_generation_addresses { config_models::network::Network, models::blockchain::{ shared::Hash, - transaction::{amount::Amount, transaction_kernel::PubScriptHashAndInput, utxo::Utxo}, + transaction::{amount::Amount, utxo::Utxo}, }, tests::shared::make_mock_transaction, }; @@ -594,22 +571,19 @@ mod test_generation_addresses { }; let sender_randomness: Digest = random(); - let (pubscript, pubscript_input) = receiving_address - .generate_pubscript_and_input(&utxo, sender_randomness) + let public_announcement = receiving_address + .generate_public_announcement(&utxo, sender_randomness) .unwrap(); let mut mock_tx = make_mock_transaction(vec![], vec![]); assert!(spending_key.scan_for_announced_utxos(&mock_tx).is_empty()); // Add a pubscript for our keys and verify that they are recognized - assert!(pubscript_input_is_marked(&pubscript_input)); + assert!(public_announcement_is_marked(&public_announcement)); mock_tx .kernel - .pubscript_hashes_and_inputs - .push(PubScriptHashAndInput { - pubscript_hash: Hash::hash(&pubscript), - pubscript_input, - }); + .public_announcements + .push(public_announcement); let announced_txs = spending_key.scan_for_announced_utxos(&mock_tx); assert_eq!(1, announced_txs.len()); diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index febf40bc9..e6276b34a 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -353,7 +353,7 @@ mod wallet_tests { use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::amount::{Amount, AmountLike}; use crate::models::blockchain::transaction::utxo::{LockScript, Utxo}; - use crate::models::blockchain::transaction::PubScript; + use crate::models::blockchain::transaction::PublicAnnouncement; use crate::models::state::wallet::utxo_notification_pool::UtxoNotifier; use crate::models::state::UtxoReceiverData; use crate::tests::shared::{ @@ -708,8 +708,7 @@ mod wallet_tests { }, sender_randomness: random(), receiver_privacy_digest: other_wallet_recipient_address.privacy_digest, - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), }]; let input_utxos_mps_keys = two_utxos .into_iter() @@ -776,8 +775,7 @@ mod wallet_tests { let (mut block_1, _, _) = make_mock_block(&genesis_block, None, own_address); let receiver_data_12_to_other = UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: own_address.privacy_digest, sender_randomness: premine_receiver_global_state .wallet_state @@ -792,8 +790,7 @@ mod wallet_tests { }, }; let receiver_data_one_to_other = UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: own_address.privacy_digest, sender_randomness: premine_receiver_global_state .wallet_state @@ -1037,8 +1034,7 @@ mod wallet_tests { ); let receiver_data_six = UtxoReceiverData { - pubscript: PubScript::default(), - pubscript_input: vec![], + public_announcement: PublicAnnouncement::default(), receiver_privacy_digest: own_address.privacy_digest, utxo: Utxo { coins: Into::::into(6).to_native_coins(), diff --git a/src/rpc_server.rs b/src/rpc_server.rs index eccca3ef5..0eaace24d 100644 --- a/src/rpc_server.rs +++ b/src/rpc_server.rs @@ -497,9 +497,9 @@ impl RPC for NeptuneRPCServer { // TODO: Allow user to set fee here. Don't set it automatically as we want the user // to be in control of this. But we could add an endpoint to get recommended fee // density. - let (pubscript, pubscript_input) = - match address.generate_pubscript_and_input(&utxo, sender_randomness) { - Ok((ps, inp)) => (ps, inp), + let public_announcement = + match address.generate_public_announcement(&utxo, sender_randomness) { + Ok(pa) => pa, Err(_) => { tracing::error!( "Failed to generate transaction because could not encrypt to address." @@ -511,8 +511,7 @@ impl RPC for NeptuneRPCServer { utxo, sender_randomness, receiver_privacy_digest, - pubscript, - pubscript_input, + public_announcement, })] .to_vec(); diff --git a/src/tests/shared.rs b/src/tests/shared.rs index e8f992447..7e8358b0f 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -51,14 +51,14 @@ use crate::models::blockchain::transaction; use crate::models::blockchain::transaction::amount::pseudorandom_amount; use crate::models::blockchain::transaction::amount::Amount; use crate::models::blockchain::transaction::transaction_kernel::pseudorandom_option; -use crate::models::blockchain::transaction::transaction_kernel::pseudorandom_pubscript_struct; +use crate::models::blockchain::transaction::transaction_kernel::pseudorandom_public_announcement; use crate::models::blockchain::transaction::transaction_kernel::pseudorandom_transaction_kernel; -use crate::models::blockchain::transaction::transaction_kernel::PubScriptHashAndInput; use crate::models::blockchain::transaction::transaction_kernel::TransactionKernel; use crate::models::blockchain::transaction::utxo::TypeScript; use crate::models::blockchain::transaction::validity::removal_records_integrity::RemovalRecordsIntegrityWitness; use crate::models::blockchain::transaction::validity::TransactionValidationLogic; use crate::models::blockchain::transaction::PrimitiveWitness; +use crate::models::blockchain::transaction::PublicAnnouncement; use crate::models::blockchain::transaction::Witness; use crate::models::blockchain::transaction::{utxo::Utxo, Transaction}; use crate::models::channel::{MainToPeerThread, PeerThreadToMain}; @@ -533,9 +533,9 @@ pub fn random_addition_record() -> AdditionRecord { pseudorandom_addition_record(rng.gen::<[u8; 32]>()) } -pub fn random_pubscript_struct() -> PubScriptHashAndInput { +pub fn random_public_announcement() -> PublicAnnouncement { let mut rng = thread_rng(); - pseudorandom_pubscript_struct(rng.gen::<[u8; 32]>()) + pseudorandom_public_announcement(rng.gen::<[u8; 32]>()) } pub fn random_amount() -> Amount { @@ -757,12 +757,9 @@ pub fn make_mock_transaction_with_generation_key( outputs.push(addition_record); } - let pubscript_hashes_and_inputs = receiver_data + let public_announcements = receiver_data .iter() - .map(|x| PubScriptHashAndInput { - pubscript_hash: Hash::hash(&x.pubscript), - pubscript_input: x.pubscript_input.clone(), - }) + .map(|x| x.public_announcement.clone()) .collect_vec(); let timestamp: u64 = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -774,7 +771,7 @@ pub fn make_mock_transaction_with_generation_key( let kernel = TransactionKernel { inputs, outputs, - pubscript_hashes_and_inputs, + public_announcements, fee, timestamp: BFieldElement::new(timestamp), coinbase: None, @@ -802,7 +799,7 @@ pub fn make_mock_transaction_with_generation_key( .collect_vec(); let pubscripts = receiver_data .iter() - .map(|rd| rd.pubscript.to_owned()) + .map(|rd| rd.public_announcement.clone()) .collect(); let output_utxos = receiver_data.into_iter().map(|rd| rd.utxo).collect(); let primitive_witness = PrimitiveWitness { @@ -812,7 +809,7 @@ pub fn make_mock_transaction_with_generation_key( lock_script_witnesses: spending_key_unlock_keys, input_membership_proofs, output_utxos, - pubscripts, + public_announcements: pubscripts, mutator_set_accumulator: tip_msa, }; let validity_logic = @@ -843,7 +840,7 @@ pub fn make_mock_transaction( kernel: TransactionKernel { inputs, outputs, - pubscript_hashes_and_inputs: vec![], + public_announcements: vec![], fee: 1.into(), timestamp, coinbase: None, @@ -875,7 +872,7 @@ pub fn make_mock_transaction_with_wallet( let kernel = TransactionKernel { inputs, outputs, - pubscript_hashes_and_inputs: vec![], + public_announcements: vec![], fee, timestamp, coinbase: None, @@ -923,7 +920,7 @@ pub fn make_mock_block( let tx_kernel = TransactionKernel { inputs: vec![], outputs: vec![coinbase_addition_record], - pubscript_hashes_and_inputs: vec![], + public_announcements: vec![], fee: Amount::zero(), timestamp: BFieldElement::new(block_timestamp), coinbase: Some(coinbase_amount), @@ -936,7 +933,7 @@ pub fn make_mock_block( lock_script_witnesses: vec![], input_membership_proofs: vec![], output_utxos: vec![coinbase_utxo.clone()], - pubscripts: vec![], + public_announcements: vec![], mutator_set_accumulator: previous_mutator_set.clone(), input_lock_scripts: vec![], }; From 82207fb7f66c4534cc111989a61eaf9789ee980d Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Wed, 24 Jan 2024 14:58:31 +0100 Subject: [PATCH 03/29] drop primitive witness from validity logic case --- src/mine_loop.rs | 2 +- src/models/blockchain/transaction/mod.rs | 4 ++-- src/models/state/mod.rs | 2 +- src/tests/shared.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index d9a8855ad..68f13c8c4 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -228,7 +228,7 @@ fn make_coinbase_transaction( ( Transaction { kernel, - witness: Witness::ValidityLogic((validity_logic, primitive_witness)), + witness: Witness::ValidityLogic(validity_logic), }, sender_randomness, ) diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index 5b48c3f1f..0effa8a32 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -92,7 +92,7 @@ impl GetSize for SingleProof { pub enum Witness { Primitive(PrimitiveWitness), SingleProof(SingleProof), - ValidityLogic((TransactionValidationLogic, PrimitiveWitness)), + ValidityLogic(TransactionValidationLogic), Faith, } @@ -223,7 +223,7 @@ impl Transaction { /// isolation, without the context of the canonical chain. pub fn is_valid(&self) -> bool { match &self.witness { - Witness::ValidityLogic((validity_logic, _)) => validity_logic.verify(), + Witness::ValidityLogic(validity_logic) => validity_logic.verify(), Witness::Primitive(primitive_witness) => { warn!("Verifying transaction by raw witness; unlock key might be exposed!"); self.validate_primitive_witness(primitive_witness) diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index e519013ac..287b230df 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -569,7 +569,7 @@ impl GlobalState { Ok(Transaction { kernel, - witness: Witness::ValidityLogic((transaction_validity_logic, primitive_witness)), + witness: Witness::ValidityLogic(transaction_validity_logic), }) } diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 7e8358b0f..32a9e756e 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -817,7 +817,7 @@ pub fn make_mock_transaction_with_generation_key( Transaction { kernel, - witness: Witness::ValidityLogic((validity_logic, primitive_witness)), + witness: Witness::ValidityLogic(validity_logic), } } @@ -941,7 +941,7 @@ pub fn make_mock_block( TransactionValidationLogic::new_from_primitive_witness(&primitive_witness, &tx_kernel); let transaction = Transaction { - witness: transaction::Witness::ValidityLogic((validity_logic, primitive_witness)), + witness: transaction::Witness::ValidityLogic(validity_logic), kernel: tx_kernel, }; From ad0da4a3615d0e0b05457772a1490e62d0f6d503 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Wed, 24 Jan 2024 15:04:02 +0100 Subject: [PATCH 04/29] rename validity logic to validation logic --- src/mine_loop.rs | 2 +- src/models/blockchain/transaction/mod.rs | 9 +++++---- src/models/state/mod.rs | 2 +- src/tests/shared.rs | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 68f13c8c4..b808faaba 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -228,7 +228,7 @@ fn make_coinbase_transaction( ( Transaction { kernel, - witness: Witness::ValidityLogic(validity_logic), + witness: Witness::ValidationLogic(validity_logic), }, sender_randomness, ) diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index 0effa8a32..f0df6e602 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -86,13 +86,12 @@ impl GetSize for SingleProof { } } -// TODO: Remove this allow once `ValidityLogic` is more sane #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] pub enum Witness { Primitive(PrimitiveWitness), SingleProof(SingleProof), - ValidityLogic(TransactionValidationLogic), + ValidationLogic(TransactionValidationLogic), Faith, } @@ -223,7 +222,7 @@ impl Transaction { /// isolation, without the context of the canonical chain. pub fn is_valid(&self) -> bool { match &self.witness { - Witness::ValidityLogic(validity_logic) => validity_logic.verify(), + Witness::ValidationLogic(validity_logic) => validity_logic.verify(), Witness::Primitive(primitive_witness) => { warn!("Verifying transaction by raw witness; unlock key might be exposed!"); self.validate_primitive_witness(primitive_witness) @@ -318,7 +317,9 @@ impl Transaction { } // TODO: Merge with recursion - (Witness::ValidityLogic(_self_vl), Witness::ValidityLogic(_other_vl)) => Witness::Faith, + (Witness::ValidationLogic(_self_vl), Witness::ValidationLogic(_other_vl)) => { + Witness::Faith + } (Witness::Faith, _) => Witness::Faith, (_, Witness::Faith) => Witness::Faith, _ => { diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 287b230df..6fc05808d 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -569,7 +569,7 @@ impl GlobalState { Ok(Transaction { kernel, - witness: Witness::ValidityLogic(transaction_validity_logic), + witness: Witness::ValidationLogic(transaction_validity_logic), }) } diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 32a9e756e..41754718f 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -817,7 +817,7 @@ pub fn make_mock_transaction_with_generation_key( Transaction { kernel, - witness: Witness::ValidityLogic(validity_logic), + witness: Witness::ValidationLogic(validity_logic), } } @@ -941,7 +941,7 @@ pub fn make_mock_block( TransactionValidationLogic::new_from_primitive_witness(&primitive_witness, &tx_kernel); let transaction = Transaction { - witness: transaction::Witness::ValidityLogic(validity_logic), + witness: transaction::Witness::ValidationLogic(validity_logic), kernel: tx_kernel, }; From e728830e70d5adee258a35bc7c13a32419bd29ad Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Wed, 24 Jan 2024 17:35:24 +0100 Subject: [PATCH 05/29] factor out abstractions related to consensus/validity --- src/mine_loop.rs | 4 +- src/models/blockchain/block/mod.rs | 3 +- src/models/blockchain/transaction/mod.rs | 52 +---- src/models/blockchain/transaction/validity.rs | 120 +--------- .../validity/kernel_to_lock_scripts.rs | 11 +- .../validity/kernel_to_type_scripts.rs | 10 +- .../transaction/validity/lockscripts_halt.rs | 14 +- .../validity/removal_records_integrity.rs | 18 +- .../transaction/validity/typescripts_halt.rs | 19 +- src/models/consensus/mod.rs | 218 ++++++++++++++++++ src/models/mod.rs | 1 + src/models/state/mod.rs | 8 +- src/tests/shared.rs | 19 +- 13 files changed, 303 insertions(+), 194 deletions(-) create mode 100644 src/models/consensus/mod.rs diff --git a/src/mine_loop.rs b/src/mine_loop.rs index b808faaba..e5c6b9489 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -213,7 +213,7 @@ fn make_coinbase_transaction( mutator_set_hash: mutator_set_accumulator.hash(), }; - let primitive_witness = PrimitiveWitness { + let primitive_witness = TransactionPrimitiveWitness { input_utxos: vec![], type_scripts: vec![TypeScript::native_coin()], input_lock_scripts: vec![], @@ -228,7 +228,7 @@ fn make_coinbase_transaction( ( Transaction { kernel, - witness: Witness::ValidationLogic(validity_logic), + witness: TransactionWitness::ValidationLogic(validity_logic), }, sender_randomness, ) diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 787b6e75f..7cb217aa1 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -32,6 +32,7 @@ use super::transaction::transaction_kernel::TransactionKernel; use super::transaction::utxo::Utxo; use super::transaction::{amount::Amount, Transaction}; use crate::models::blockchain::shared::Hash; +use crate::models::consensus::Witness; use crate::models::state::wallet::address::generation_address; use crate::models::state::wallet::WalletSecret; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; @@ -120,7 +121,7 @@ impl Block { coinbase: Some(total_premine_amount), mutator_set_hash: MutatorSetAccumulator::::new().hash(), }, - witness: super::transaction::Witness::Faith, + witness: Witness::Faith, }; for (receiving_address, amount) in premine_distribution { diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index f0df6e602..8c0f2a4e9 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -6,6 +6,7 @@ pub mod transaction_kernel; pub mod utxo; pub mod validity; +use crate::models::consensus::Witness; use anyhow::Result; use get_size::GetSize; use itertools::Itertools; @@ -16,11 +17,7 @@ use std::cmp::max; use std::hash::{Hash as StdHash, Hasher as StdHasher}; use std::time::SystemTime; use tracing::{debug, error, warn}; -use triton_vm::proof::Proof; -use triton_vm::{ - prelude::{NonDeterminism, PublicInput}, - triton_asm, -}; +use triton_vm::prelude::NonDeterminism; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::bfield_codec::BFieldCodec; use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; @@ -39,6 +36,8 @@ use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulat use crate::util_types::mutator_set::mutator_set_trait::MutatorSet; use crate::util_types::mutator_set::removal_record::RemovalRecord; +pub type TransactionWitness = Witness; + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec, Default)] pub struct PublicAnnouncement { pub message: Vec, @@ -53,7 +52,7 @@ impl PublicAnnouncement { /// The raw witness is the most primitive type of transaction witness. /// It exposes secret data and is therefore not for broadcasting. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] -pub struct PrimitiveWitness { +pub struct TransactionPrimitiveWitness { pub input_utxos: Vec, pub input_lock_scripts: Vec, pub type_scripts: Vec, @@ -64,42 +63,11 @@ pub struct PrimitiveWitness { pub mutator_set_accumulator: MutatorSetAccumulator, } -/// Single proofs are the final abstaction layer for transaction -/// witnesses. It represents the merger of a set of linked proofs -/// into one. It hides information that linked proofs expose, but -/// the downside is that it requires multiple runs of the recursive -/// prover to produce. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BFieldCodec)] -pub struct SingleProof(pub Proof); - -impl GetSize for SingleProof { - fn get_stack_size() -> usize { - std::mem::size_of::() - } - - fn get_heap_size(&self) -> usize { - self.0.get_heap_size() - } - - fn get_size(&self) -> usize { - Self::get_stack_size() + GetSize::get_heap_size(self) - } -} - -#[allow(clippy::large_enum_variant)] -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] -pub enum Witness { - Primitive(PrimitiveWitness), - SingleProof(SingleProof), - ValidationLogic(TransactionValidationLogic), - Faith, -} - #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] pub struct Transaction { pub kernel: TransactionKernel, - pub witness: Witness, + pub witness: TransactionWitness, } /// Make `Transaction` hashable with `StdHash` for using it in `HashMap`. @@ -274,7 +242,7 @@ impl Transaction { let merged_witness = match (&self.witness, &other.witness) { (Witness::Primitive(self_witness), Witness::Primitive(other_witness)) => { - Witness::Primitive(PrimitiveWitness { + Witness::Primitive(TransactionPrimitiveWitness { input_utxos: [ self_witness.input_utxos.clone(), other_witness.input_utxos.clone(), @@ -358,7 +326,7 @@ impl Transaction { .all(|rr| rr.validate(&mutator_set_accumulator.kernel)) } - fn validate_primitive_witness(&self, primitive_witness: &PrimitiveWitness) -> bool { + fn validate_primitive_witness(&self, primitive_witness: &TransactionPrimitiveWitness) -> bool { // verify lock scripts for (lock_script, secret_input) in primitive_witness .input_lock_scripts @@ -525,7 +493,7 @@ mod witness_tests { #[test] fn decode_encode_test_empty() { - let primitive_witness = PrimitiveWitness { + let primitive_witness = TransactionPrimitiveWitness { input_utxos: vec![], type_scripts: vec![], input_lock_scripts: vec![], @@ -537,7 +505,7 @@ mod witness_tests { }; let encoded = primitive_witness.encode(); - let decoded = *PrimitiveWitness::decode(&encoded).unwrap(); + let decoded = *TransactionPrimitiveWitness::decode(&encoded).unwrap(); assert_eq!(primitive_witness, decoded); } } diff --git a/src/models/blockchain/transaction/validity.rs b/src/models/blockchain/transaction/validity.rs index f87f944fe..8645abc22 100644 --- a/src/models/blockchain/transaction/validity.rs +++ b/src/models/blockchain/transaction/validity.rs @@ -10,20 +10,23 @@ pub mod typescripts_halt; use anyhow::{Ok, Result}; use get_size::GetSize; use serde::{Deserialize, Serialize}; -use tracing::{debug, info, warn}; -use triton_vm::prelude::{Claim, NonDeterminism, PublicInput, StarkParameters}; +use tracing::info; +use triton_vm::prelude::{Claim, NonDeterminism}; use triton_vm::program::Program; use triton_vm::proof::Proof; use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::bfield_codec::BFieldCodec; +use crate::models::consensus::ValidationLogic; + use self::lockscripts_halt::LockScriptsHalt; use self::removal_records_integrity::RemovalRecordsIntegrity; use self::{ kernel_to_lock_scripts::KernelToLockScripts, kernel_to_type_scripts::KernelToTypeScripts, typescripts_halt::TypeScriptsHalt, }; -use super::{transaction_kernel::TransactionKernel, PrimitiveWitness}; +use super::transaction_kernel::TransactionKernel; +use super::TransactionPrimitiveWitness; pub trait SecretWitness: Clone + Serialize + PartialEq + Eq + GetSize + BFieldCodec + Sized @@ -88,118 +91,9 @@ pub struct TransactionValidationLogic { pub type_scripts_halt: TypeScriptsHalt, } -pub trait ValidationLogic { - fn subprogram(&self) -> Program; - fn support(&self) -> ClaimSupport; - fn claim(&self) -> Claim; - - /// Update witness secret witness to proof - fn upgrade(&mut self, _proof: Proof) { - todo!() - } - - fn new_from_primitive_witness( - primitive_witness: &PrimitiveWitness, - tx_kernel: &TransactionKernel, - ) -> Self; - - /// Prove the claim. - fn prove(&mut self) -> Result<()> { - match &self.support() { - ClaimSupport::Proof(_) => { - // nothing to do; proof already exists - Ok(()) - } - ClaimSupport::SecretWitness(witness) => { - // Run program before proving - self.subprogram() - .run( - self.claim().public_input().into(), - witness.nondeterminism().clone(), - ) - .expect("Program execution prior to proving must succeed"); - - let proof = triton_vm::prove( - StarkParameters::default(), - &self.claim(), - &self.subprogram(), - witness.nondeterminism().clone(), - ) - .expect("Proving integrity of removal records must succeed."); - self.upgrade(proof); - Ok(()) - } - ClaimSupport::DummySupport => { - // nothing to do - warn!("Trying to prove claim supported by dummy support"); - Ok(()) - } - ClaimSupport::MultipleSupports(_supports) => { - warn!("Trying to prove claim with multiple supports; not supported yet"); - Ok(()) - } - } - } - - /// Verify the claim. - fn verify(&self) -> bool { - use std::result::Result::Ok; - match &self.support() { - ClaimSupport::Proof(proof) => { - triton_vm::verify(StarkParameters::default(), &self.claim(), proof) - } - ClaimSupport::SecretWitness(w) => { - let nondeterminism = w.nondeterminism(); - let input = &self.claim().input; - let vm_result = w - .subprogram() - .run(PublicInput::new(input.to_vec()), nondeterminism); - match vm_result { - Ok(observed_output) => { - let found_expected_output = observed_output == self.claim().output; - if !found_expected_output { - warn!("Observed output does not match claimed output for RRI"); - debug!("Got output: {found_expected_output}"); - } - - found_expected_output - } - Err(err) => { - warn!("VM execution for removal records integrity did not halt gracefully"); - debug!("Last state was: {err}"); - false - } - } - } - ClaimSupport::DummySupport => { - warn!("dummy support encountered"); - false - } - ClaimSupport::MultipleSupports(secret_witnesses) => { - let claim = self.claim(); - for witness in secret_witnesses.iter() { - let vm_result = witness.subprogram().run( - PublicInput::new(claim.input.to_vec()), - witness.nondeterminism(), - ); - match vm_result { - Ok(_) => (), - Err(err) => { - warn!("Multiple-support witness failed to validate: {err}"); - return false; - } - } - } - - true - } - } - } -} - impl TransactionValidationLogic { pub fn new_from_primitive_witness( - primitive_witness: &PrimitiveWitness, + primitive_witness: &TransactionPrimitiveWitness, tx_kernel: &TransactionKernel, ) -> Self { let lock_scripts_halt = diff --git a/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs b/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs index 6cc613aa5..9a9eec750 100644 --- a/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs +++ b/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs @@ -1,12 +1,13 @@ use crate::prelude::{triton_vm, twenty_first}; +use crate::models::blockchain::transaction::TransactionPrimitiveWitness; use crate::models::blockchain::transaction::{ transaction_kernel::{TransactionKernel, TransactionKernelField}, utxo::Utxo, - PrimitiveWitness, }; -use super::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; +use crate::models::consensus::{ClaimSupport, SupportedClaim}; +use crate::models::consensus::{SecretWitness, ValidationLogic}; use get_size::GetSize; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -46,8 +47,12 @@ impl KernelToLockScripts { } impl ValidationLogic for KernelToLockScripts { + type PrimitiveWitness = TransactionPrimitiveWitness; + + type Kernel = TransactionKernel; + fn new_from_primitive_witness( - primitive_witness: &PrimitiveWitness, + primitive_witness: &TransactionPrimitiveWitness, tx_kernel: &TransactionKernel, ) -> Self { let claim = Claim { diff --git a/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs b/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs index 7197abd0a..8d54f100c 100644 --- a/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs +++ b/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs @@ -1,8 +1,10 @@ +use crate::models::blockchain::transaction::transaction_kernel::TransactionKernel; use crate::prelude::{triton_vm, twenty_first}; use crate::models::blockchain::transaction::utxo::TypeScript; +use crate::models::blockchain::transaction::TransactionPrimitiveWitness; +use crate::models::consensus::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; -use super::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; use get_size::GetSize; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -42,8 +44,12 @@ impl KernelToTypeScripts { } impl ValidationLogic for KernelToTypeScripts { + type PrimitiveWitness = TransactionPrimitiveWitness; + + type Kernel = TransactionKernel; + fn new_from_primitive_witness( - primitive_witness: &crate::models::blockchain::transaction::PrimitiveWitness, + primitive_witness: &crate::models::blockchain::transaction::TransactionPrimitiveWitness, tx_kernel: &crate::models::blockchain::transaction::transaction_kernel::TransactionKernel, ) -> Self { let mut type_script_digests = primitive_witness diff --git a/src/models/blockchain/transaction/validity/lockscripts_halt.rs b/src/models/blockchain/transaction/validity/lockscripts_halt.rs index 95aa4c5a2..6ed1ae109 100644 --- a/src/models/blockchain/transaction/validity/lockscripts_halt.rs +++ b/src/models/blockchain/transaction/validity/lockscripts_halt.rs @@ -6,9 +6,11 @@ use serde::{Deserialize, Serialize}; use triton_vm::prelude::{BFieldElement, Claim, NonDeterminism, Program}; use twenty_first::shared_math::bfield_codec::BFieldCodec; -use super::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; -use crate::models::blockchain::transaction::{ - transaction_kernel::TransactionKernel, utxo::LockScript, PrimitiveWitness, +use crate::models::{ + blockchain::transaction::{ + transaction_kernel::TransactionKernel, utxo::LockScript, TransactionPrimitiveWitness, + }, + consensus::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}, }; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] @@ -33,8 +35,12 @@ pub struct LockScriptsHalt { } impl ValidationLogic for LockScriptsHalt { + type PrimitiveWitness = TransactionPrimitiveWitness; + + type Kernel = TransactionKernel; + fn new_from_primitive_witness( - primitive_witness: &PrimitiveWitness, + primitive_witness: &TransactionPrimitiveWitness, tx_kernel: &TransactionKernel, ) -> LockScriptsHalt { let program_and_program_digests_and_spending_keys = primitive_witness diff --git a/src/models/blockchain/transaction/validity/removal_records_integrity.rs b/src/models/blockchain/transaction/validity/removal_records_integrity.rs index dff6f9bd0..458efc46b 100644 --- a/src/models/blockchain/transaction/validity/removal_records_integrity.rs +++ b/src/models/blockchain/transaction/validity/removal_records_integrity.rs @@ -14,15 +14,12 @@ use twenty_first::{ util_types::{algebraic_hasher::AlgebraicHasher, mmr::mmr_accumulator::MmrAccumulator}, }; -use crate::models::blockchain::transaction::validity::SecretWitness; +use crate::models::consensus::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; use crate::{ models::blockchain::{ shared::Hash, transaction::{ - transaction_kernel::TransactionKernel, - utxo::Utxo, - validity::{ClaimSupport, SupportedClaim, ValidationLogic}, - PrimitiveWitness, + transaction_kernel::TransactionKernel, utxo::Utxo, TransactionPrimitiveWitness, }, }, util_types::mutator_set::ms_membership_proof::MsMembershipProof, @@ -50,7 +47,10 @@ pub struct RemovalRecordsIntegrityWitness { } impl RemovalRecordsIntegrityWitness { - pub fn new(primitive_witness: &PrimitiveWitness, tx_kernel: &TransactionKernel) -> Self { + pub fn new( + primitive_witness: &TransactionPrimitiveWitness, + tx_kernel: &TransactionKernel, + ) -> Self { Self { input_utxos: primitive_witness.input_utxos.clone(), membership_proofs: primitive_witness.input_membership_proofs.clone(), @@ -92,8 +92,12 @@ pub struct RemovalRecordsIntegrity { } impl ValidationLogic for RemovalRecordsIntegrity { + type PrimitiveWitness = TransactionPrimitiveWitness; + + type Kernel = TransactionKernel; + fn new_from_primitive_witness( - primitive_witness: &crate::models::blockchain::transaction::PrimitiveWitness, + primitive_witness: &crate::models::blockchain::transaction::TransactionPrimitiveWitness, tx_kernel: &crate::models::blockchain::transaction::transaction_kernel::TransactionKernel, ) -> Self { let removal_records_integrity_witness = diff --git a/src/models/blockchain/transaction/validity/typescripts_halt.rs b/src/models/blockchain/transaction/validity/typescripts_halt.rs index b5694d7ac..777788c6a 100644 --- a/src/models/blockchain/transaction/validity/typescripts_halt.rs +++ b/src/models/blockchain/transaction/validity/typescripts_halt.rs @@ -6,14 +6,15 @@ use serde::{Deserialize, Serialize}; use triton_vm::prelude::{BFieldElement, Claim, NonDeterminism, Program}; use twenty_first::shared_math::bfield_codec::BFieldCodec; -use crate::models::blockchain::transaction::{ - transaction_kernel::TransactionKernel, - utxo::{TypeScript, Utxo}, - PrimitiveWitness, +use crate::models::{ + blockchain::transaction::{ + transaction_kernel::TransactionKernel, + utxo::{TypeScript, Utxo}, + TransactionPrimitiveWitness, + }, + consensus::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}, }; -use super::{ClaimSupport, SecretWitness, SupportedClaim, ValidationLogic}; - #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] pub struct TypeScriptHaltsWitness { type_script: TypeScript, @@ -46,8 +47,12 @@ impl TypeScriptsHalt { } impl ValidationLogic for TypeScriptsHalt { + type PrimitiveWitness = TransactionPrimitiveWitness; + + type Kernel = TransactionKernel; + fn new_from_primitive_witness( - primitive_witness: &PrimitiveWitness, + primitive_witness: &TransactionPrimitiveWitness, tx_kernel: &TransactionKernel, ) -> Self { let claim = Claim { diff --git a/src/models/consensus/mod.rs b/src/models/consensus/mod.rs new file mode 100644 index 000000000..48198e6b4 --- /dev/null +++ b/src/models/consensus/mod.rs @@ -0,0 +1,218 @@ +use anyhow::Result; +use get_size::GetSize; +use serde::Deserialize; +use serde::Serialize; +use tasm_lib::triton_vm; +use tasm_lib::twenty_first::shared_math::b_field_element::BFieldElement; +use tasm_lib::twenty_first::shared_math::bfield_codec::BFieldCodec; +use tracing::{debug, warn}; +use triton_vm::prelude::Claim; +use triton_vm::prelude::NonDeterminism; +use triton_vm::prelude::Program; +use triton_vm::prelude::Proof; +use triton_vm::prelude::PublicInput; +use triton_vm::prelude::StarkParameters; + +/// This file contains abstractions for verifying consensus logic using TritonVM STARK +/// proofs. The concrete logic is specified in the directories `transaction` and `block`. + +/// A Witness is any data that supports the truth of a claim, typically related to the +/// validity of a block or transaction. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] +pub enum Witness { + /// All the first-order witness data that supports the validity claim, including + /// sensitive secrets. + Primitive(PrimitiveWitness), + /// A decomposition of the validity claim into smaller claims, each one of which has + /// its own supporting witness. + ValidationLogic(ValidationLogic), + /// A single proof for the entire claim, typically produced via recursion. + SingleProof(SingleProof), + /// As we do not have recursion yet, sometimes we just need to take things on faith. + /// This must be depracated before mainnet launch! + Faith, +} + +/// Single proofs are the final abstaction layer for +/// witnesses. They represent the merger of a set of linked proofs +/// into one. They hide information that linked proofs expose, but +/// the downside is that their production requires multiple runs of the recursive +/// prover. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BFieldCodec)] +pub struct SingleProof(pub Proof); + +impl GetSize for SingleProof { + fn get_stack_size() -> usize { + std::mem::size_of::() + } + + fn get_heap_size(&self) -> usize { + self.0.get_heap_size() + } + + fn get_size(&self) -> usize { + Self::get_stack_size() + GetSize::get_heap_size(self) + } +} + +pub trait SecretWitness: + Clone + Serialize + PartialEq + Eq + GetSize + BFieldCodec + Sized +{ + /// The non-determinism for the VM that this witness corresponds to + fn nondeterminism(&self) -> NonDeterminism; + + /// Returns the subprogram + fn subprogram(&self) -> Program; +} + +/// When a claim to validity decomposes into multiple subclaims via variant +/// `ValidationLogic` of `Witness`, those subclaims are `SupportedClaims`. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] +pub struct SupportedClaim { + pub claim: crate::triton_vm::proof::Claim, + pub support: ClaimSupport, +} + +/// When a claimto validity decomposes into multiple subclaims via variant +/// `ValidationLogic` of `Witness`, those subclaims pertain to the graceful halting of +/// programs ("subprograms"), which is itself supported by either a proof or some witness +/// that can help the prover produce one. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] +pub enum ClaimSupport { + Proof(Proof), + MultipleSupports(Vec), + SecretWitness(SubprogramWitness), + DummySupport, // TODO: Remove this when all claims are implemented +} + +/// SupportedClaim is a helper struct. It +/// encodes a Claim with an optional witness. + +impl SupportedClaim { + // TODO: REMOVE when all validity logic is implemented + pub fn dummy() -> Self { + let dummy_claim = crate::triton_vm::proof::Claim { + input: Default::default(), + output: Default::default(), + program_digest: Default::default(), + }; + + Self { + claim: dummy_claim, + support: ClaimSupport::DummySupport, + } + } +} + +/// A trait for proving and verifying claims to validity of transactions or blocks, +/// sometimes with and sometimes without witness data. +pub trait ValidationLogic { + type PrimitiveWitness; + type Kernel; + + fn subprogram(&self) -> Program; + fn support(&self) -> ClaimSupport; + fn claim(&self) -> Claim; + + /// Update witness secret witness to proof + fn upgrade(&mut self, _proof: Proof) { + todo!() + } + + fn new_from_primitive_witness( + primitive_witness: &Self::PrimitiveWitness, + tx_kernel: &Self::Kernel, + ) -> Self; + + /// Prove the claim. + fn prove(&mut self) -> Result<()> { + match &self.support() { + ClaimSupport::Proof(_) => { + // nothing to do; proof already exists + Ok(()) + } + ClaimSupport::SecretWitness(witness) => { + // Run program before proving + self.subprogram() + .run( + self.claim().public_input().into(), + witness.nondeterminism().clone(), + ) + .expect("Program execution prior to proving must succeed"); + + let proof = triton_vm::prove( + StarkParameters::default(), + &self.claim(), + &self.subprogram(), + witness.nondeterminism().clone(), + ) + .expect("Proving integrity of removal records must succeed."); + self.upgrade(proof); + Ok(()) + } + ClaimSupport::DummySupport => { + // nothing to do + warn!("Trying to prove claim supported by dummy support"); + Ok(()) + } + ClaimSupport::MultipleSupports(_supports) => { + warn!("Trying to prove claim with multiple supports; not supported yet"); + Ok(()) + } + } + } + + /// Verify the claim. + fn verify(&self) -> bool { + match &self.support() { + ClaimSupport::Proof(proof) => { + triton_vm::verify(StarkParameters::default(), &self.claim(), proof) + } + ClaimSupport::SecretWitness(w) => { + let nondeterminism = w.nondeterminism(); + let input = &self.claim().input; + let vm_result = w + .subprogram() + .run(PublicInput::new(input.to_vec()), nondeterminism); + match vm_result { + Ok(observed_output) => { + let found_expected_output = observed_output == self.claim().output; + if !found_expected_output { + warn!("Observed output does not match claimed output for RRI"); + debug!("Got output: {found_expected_output}"); + } + + found_expected_output + } + Err(err) => { + warn!("VM execution for removal records integrity did not halt gracefully"); + debug!("Last state was: {err}"); + false + } + } + } + ClaimSupport::DummySupport => { + warn!("dummy support encountered"); + false + } + ClaimSupport::MultipleSupports(secret_witnesses) => { + let claim = self.claim(); + for witness in secret_witnesses.iter() { + let vm_result = witness.subprogram().run( + PublicInput::new(claim.input.to_vec()), + witness.nondeterminism(), + ); + match vm_result { + Ok(_) => (), + Err(err) => { + warn!("Multiple-support witness failed to validate: {err}"); + return false; + } + } + } + + true + } + } + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 79ca18d06..60b29c456 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,6 +1,7 @@ pub mod big_array; pub mod blockchain; pub mod channel; +pub mod consensus; pub mod database; pub mod peer; pub mod shared; diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 6fc05808d..a6e4099df 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -26,13 +26,15 @@ use super::blockchain::block::block_height::BlockHeight; use super::blockchain::block::Block; use super::blockchain::transaction::transaction_kernel::TransactionKernel; use super::blockchain::transaction::utxo::{LockScript, TypeScript, Utxo}; -use super::blockchain::transaction::validity::{TransactionValidationLogic, ValidationLogic}; +use super::blockchain::transaction::validity::TransactionValidationLogic; use super::blockchain::transaction::{ amount::{Amount, Sign}, Transaction, }; -use super::blockchain::transaction::{PrimitiveWitness, PublicAnnouncement, Witness}; +use super::blockchain::transaction::{PublicAnnouncement, TransactionPrimitiveWitness}; +use super::consensus::ValidationLogic; use crate::config_models::cli_args; +use crate::models::consensus::Witness; use crate::models::peer::HandshakeData; use crate::models::state::wallet::monitored_utxo::MonitoredUtxo; use crate::models::state::wallet::utxo_notification_pool::ExpectedUtxo; @@ -538,7 +540,7 @@ impl GlobalState { // is here the spending key reversed. let mut secret_input = spending_key.unlock_key.encode(); secret_input.reverse(); - let mut primitive_witness = PrimitiveWitness { + let mut primitive_witness = TransactionPrimitiveWitness { input_utxos, input_lock_scripts, type_scripts, diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 41754718f..c7338d428 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -47,7 +47,6 @@ use crate::models::blockchain::block::block_body::BlockBody; use crate::models::blockchain::block::block_header::BlockHeader; use crate::models::blockchain::block::block_header::TARGET_BLOCK_INTERVAL; use crate::models::blockchain::block::{block_height::BlockHeight, Block}; -use crate::models::blockchain::transaction; use crate::models::blockchain::transaction::amount::pseudorandom_amount; use crate::models::blockchain::transaction::amount::Amount; use crate::models::blockchain::transaction::transaction_kernel::pseudorandom_option; @@ -57,9 +56,9 @@ use crate::models::blockchain::transaction::transaction_kernel::TransactionKerne use crate::models::blockchain::transaction::utxo::TypeScript; use crate::models::blockchain::transaction::validity::removal_records_integrity::RemovalRecordsIntegrityWitness; use crate::models::blockchain::transaction::validity::TransactionValidationLogic; -use crate::models::blockchain::transaction::PrimitiveWitness; use crate::models::blockchain::transaction::PublicAnnouncement; -use crate::models::blockchain::transaction::Witness; +use crate::models::blockchain::transaction::TransactionPrimitiveWitness; +use crate::models::blockchain::transaction::TransactionWitness; use crate::models::blockchain::transaction::{utxo::Utxo, Transaction}; use crate::models::channel::{MainToPeerThread, PeerThreadToMain}; use crate::models::database::BlockIndexKey; @@ -802,7 +801,7 @@ pub fn make_mock_transaction_with_generation_key( .map(|rd| rd.public_announcement.clone()) .collect(); let output_utxos = receiver_data.into_iter().map(|rd| rd.utxo).collect(); - let primitive_witness = PrimitiveWitness { + let primitive_witness = TransactionPrimitiveWitness { input_utxos, type_scripts, input_lock_scripts, @@ -817,7 +816,7 @@ pub fn make_mock_transaction_with_generation_key( Transaction { kernel, - witness: Witness::ValidationLogic(validity_logic), + witness: TransactionWitness::ValidationLogic(validity_logic), } } @@ -846,7 +845,7 @@ pub fn make_mock_transaction( coinbase: None, mutator_set_hash: random(), }, - witness: transaction::Witness::Faith, + witness: TransactionWitness::Faith, } } @@ -881,7 +880,7 @@ pub fn make_mock_transaction_with_wallet( Transaction { kernel, - witness: transaction::Witness::Faith, + witness: TransactionWitness::Faith, } } @@ -927,7 +926,7 @@ pub fn make_mock_block( mutator_set_hash: previous_mutator_set.hash(), }; - let primitive_witness = PrimitiveWitness { + let primitive_witness = TransactionPrimitiveWitness { input_utxos: vec![], type_scripts: vec![TypeScript::native_coin()], lock_script_witnesses: vec![], @@ -937,11 +936,11 @@ pub fn make_mock_block( mutator_set_accumulator: previous_mutator_set.clone(), input_lock_scripts: vec![], }; - let validity_logic = + let validation_logic = TransactionValidationLogic::new_from_primitive_witness(&primitive_witness, &tx_kernel); let transaction = Transaction { - witness: transaction::Witness::ValidationLogic(validity_logic), + witness: TransactionWitness::ValidationLogic(validation_logic), kernel: tx_kernel, }; From af4bb001c060353eae39c521bb87b407d4d7daf1 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Thu, 25 Jan 2024 17:40:10 +0100 Subject: [PATCH 06/29] refactor(consensus): introduce skeleton for block validation logic BREAKING CHANGE --- src/bin/dashboard_src/overview_screen.rs | 4 +- src/main_loop.rs | 36 +- src/mine_loop.rs | 48 ++- src/models/blockchain/block/block_body.rs | 5 +- src/models/blockchain/block/block_header.rs | 3 +- src/models/blockchain/block/block_height.rs | 5 +- src/models/blockchain/block/block_kernel.rs | 17 + src/models/blockchain/block/mod.rs | 175 ++++---- src/models/blockchain/block/transfer_block.rs | 2 + src/models/blockchain/block/validity.rs | 41 ++ .../block/validity/coinbase_is_valid.rs | 32 ++ .../correct_control_parameter_update.rs | 31 ++ .../block/validity/correct_mmr_update.rs | 32 ++ .../validity/correct_mutator_set_update.rs | 34 ++ .../block/validity/predecessor_is_valid.rs | 31 ++ .../block/validity/transaction_is_valid.rs | 31 ++ src/models/blockchain/transaction/mod.rs | 18 +- src/models/blockchain/transaction/validity.rs | 15 +- src/models/peer.rs | 12 +- src/models/shared.rs | 8 +- src/models/state/archival_state.rs | 389 +++++++++++------- src/models/state/mempool.rs | 5 +- src/models/state/mod.rs | 110 +++-- src/models/state/wallet/mod.rs | 92 +++-- src/models/state/wallet/wallet_state.rs | 87 ++-- src/peer_loop.rs | 147 ++++--- src/rpc_server.rs | 2 + src/tests/shared.rs | 29 +- 28 files changed, 968 insertions(+), 473 deletions(-) create mode 100644 src/models/blockchain/block/block_kernel.rs create mode 100644 src/models/blockchain/block/validity.rs create mode 100644 src/models/blockchain/block/validity/coinbase_is_valid.rs create mode 100644 src/models/blockchain/block/validity/correct_control_parameter_update.rs create mode 100644 src/models/blockchain/block/validity/correct_mmr_update.rs create mode 100644 src/models/blockchain/block/validity/correct_mutator_set_update.rs create mode 100644 src/models/blockchain/block/validity/predecessor_is_valid.rs create mode 100644 src/models/blockchain/block/validity/transaction_is_valid.rs diff --git a/src/bin/dashboard_src/overview_screen.rs b/src/bin/dashboard_src/overview_screen.rs index b94ca6640..7021b267f 100644 --- a/src/bin/dashboard_src/overview_screen.rs +++ b/src/bin/dashboard_src/overview_screen.rs @@ -102,7 +102,9 @@ impl OverviewData { is_mining: Some(false), syncing: false, block_header: Some( - neptune_core::models::blockchain::block::Block::genesis_block().header, + neptune_core::models::blockchain::block::Block::genesis_block() + .kernel + .header, ), block_interval: Some(558u64), diff --git a/src/main_loop.rs b/src/main_loop.rs index fc1d4d93c..81bde1b26 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -307,17 +307,18 @@ impl MainLoopHandler { // and we assume it is the longest chain even though we could have received // a block from a peer thread before this event is triggered. let new_block = new_block_info.block; - info!("Miner found new block: {}", new_block.header.height); + info!("Miner found new block: {}", new_block.kernel.header.height); // Store block in database // This block spans global state write lock for updating. let mut global_state_mut = self.global_state_lock.lock_guard_mut().await; let (tip_hash, tip_proof_of_work_family) = ( - global_state_mut.chain.light_state().hash, + global_state_mut.chain.light_state().hash(), global_state_mut .chain .light_state() + .kernel .header .proof_of_work_family, ); @@ -325,8 +326,9 @@ impl MainLoopHandler { // If we received a new block from a peer and updated the global state before this message from the miner was handled, // we abort and do not store the newly found block. The newly found block has to be the direct descendant of what this // node considered the most canonical block. - let block_is_new = tip_proof_of_work_family < new_block.header.proof_of_work_family - && new_block.header.prev_block_digest == tip_hash; + let block_is_new = tip_proof_of_work_family + < new_block.kernel.header.proof_of_work_family + && new_block.kernel.header.prev_block_digest == tip_hash; if !block_is_new { warn!("Got new block from miner thread that was not child of tip. Discarding."); return Ok(()); @@ -380,11 +382,12 @@ impl MainLoopHandler { let tip_proof_of_work_family = global_state_mut .chain .light_state() + .kernel .header .proof_of_work_family; let block_is_new = - tip_proof_of_work_family < last_block.header.proof_of_work_family; + tip_proof_of_work_family < last_block.kernel.header.proof_of_work_family; if !block_is_new { warn!("Blocks were not new. Not storing blocks."); @@ -397,7 +400,7 @@ impl MainLoopHandler { // Get out of sync mode if needed if global_state_mut.net.syncing { let stay_in_sync_mode = stay_in_sync_mode( - &last_block.header, + &last_block.kernel.header, &main_loop_state.sync_state, global_state_mut.cli.max_number_of_blocks_before_syncing, ); @@ -410,10 +413,12 @@ impl MainLoopHandler { for new_block in blocks { debug!( "Storing block {} in database. Height: {}, Mined: {}", - new_block.hash.emojihash(), - new_block.header.height, - crate::utc_timestamp_to_localtime(new_block.header.timestamp.value()) - .to_string() + new_block.hash().emojihash(), + new_block.kernel.header.height, + crate::utc_timestamp_to_localtime( + new_block.kernel.header.timestamp.value() + ) + .to_string() ); global_state_mut.store_block(new_block).await?; @@ -716,9 +721,14 @@ impl MainLoopHandler { // Check when latest batch of blocks was requested let (current_block_hash, current_block_height, current_block_proof_of_work_family) = ( - global_state.chain.light_state().hash, - global_state.chain.light_state().header.height, - global_state.chain.light_state().header.proof_of_work_family, + global_state.chain.light_state().hash(), + global_state.chain.light_state().kernel.header.height, + global_state + .chain + .light_state() + .kernel + .header + .proof_of_work_family, ); let (peer_to_sanction, try_new_request): (Option, bool) = main_loop_state diff --git a/src/mine_loop.rs b/src/mine_loop.rs index e5c6b9489..5b9af6b99 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -47,8 +47,11 @@ fn make_block_template( ) -> (BlockHeader, BlockBody) { let additions = transaction.kernel.outputs.clone(); let removals = transaction.kernel.inputs.clone(); - let mut next_mutator_set_accumulator: MutatorSetAccumulator = - previous_block.body.next_mutator_set_accumulator.clone(); + let mut next_mutator_set_accumulator: MutatorSetAccumulator = previous_block + .kernel + .body + .next_mutator_set_accumulator + .clone(); // Apply the mutator set update to the mutator set accumulator // This function mutates the MS accumulator that is given as argument to @@ -61,22 +64,25 @@ fn make_block_template( let block_body: BlockBody = BlockBody { transaction, next_mutator_set_accumulator: next_mutator_set_accumulator.clone(), - previous_mutator_set_accumulator: previous_block.body.next_mutator_set_accumulator.clone(), - stark_proof: vec![], + previous_mutator_set_accumulator: previous_block + .kernel + .body + .next_mutator_set_accumulator + .clone(), }; let zero = BFieldElement::zero(); let new_pow_line: U32s<5> = - previous_block.header.proof_of_work_family + previous_block.header.difficulty; + previous_block.kernel.header.proof_of_work_family + previous_block.kernel.header.difficulty; let mutator_set_commitment: Digest = next_mutator_set_accumulator.hash(); - let next_block_height = previous_block.header.height.next(); + let next_block_height = previous_block.kernel.header.height.next(); let mut block_timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Got bad time timestamp in mining process") .as_millis() as u64; - if block_timestamp < previous_block.header.timestamp.value() { + if block_timestamp < previous_block.kernel.header.timestamp.value() { warn!("Received block is timestamped in the future; mining on future-timestamped block."); - block_timestamp = previous_block.header.timestamp.value() + 1; + block_timestamp = previous_block.kernel.header.timestamp.value() + 1; } let difficulty: U32s<5> = Block::difficulty_control(previous_block, block_timestamp); @@ -84,7 +90,7 @@ fn make_block_template( version: zero, height: next_block_height, mutator_set_hash: mutator_set_commitment, - prev_block_digest: Hash::hash(&previous_block.header), + prev_block_digest: Hash::hash(&previous_block.kernel.header), timestamp: BFieldElement::new(block_timestamp), nonce: [zero, zero, zero], max_block_size: MOCK_MAX_BLOCK_SIZE, @@ -153,13 +159,13 @@ async fn mine_block( ); let new_block_info = NewBlockFound { - block: Box::new(Block::new(block_header, block_body)), + block: Box::new(Block::new(block_header, block_body, None)), coinbase_utxo_info: Box::new(coinbase_utxo_info), }; info!( "PoW digest of new block: {}. Threshold was: {threshold}", - new_block_info.block.hash + new_block_info.block.hash() ); sender @@ -258,7 +264,7 @@ fn create_block_transaction( .wallet_secret .nth_generation_spending_key(0); let receiving_address = coinbase_recipient_spending_key.to_address(); - let next_block_height: BlockHeight = latest_block.header.height.next(); + let next_block_height: BlockHeight = latest_block.kernel.header.height.next(); let lock_script = receiving_address.lock_script(); let coinbase_amount = Block::get_mining_reward(next_block_height) + transaction_fees; @@ -269,12 +275,17 @@ fn create_block_transaction( receiving_address.privacy_digest, &global_state.wallet_state.wallet_secret, next_block_height, - latest_block.body.next_mutator_set_accumulator.clone(), + latest_block + .kernel + .body + .next_mutator_set_accumulator + .clone(), ); debug!( "Creating block transaction with mutator set hash: {}", latest_block + .kernel .body .next_mutator_set_accumulator .hash() @@ -337,7 +348,7 @@ pub async fn mine( worker_thread_tx, global_state_lock.clone(), coinbase_utxo_info, - latest_block.header.difficulty, + latest_block.kernel.header.difficulty, ); global_state_lock.set_mining(true).await; Some( @@ -371,7 +382,7 @@ pub async fn mine( mt.abort(); } latest_block = *block; - info!("Miner thread received {} block height {}", global_state_lock.lock(|s| s.cli.network).await, latest_block.header.height); + info!("Miner thread received {} block height {}", global_state_lock.lock(|s| s.cli.network).await, latest_block.kernel.header.height); } MainToMiner::Empty => (), MainToMiner::ReadyToMineNextBlock => { @@ -402,7 +413,7 @@ pub async fn mine( } }; - debug!("Worker thread reports new block of height {}", new_block_info.block.header.height); + debug!("Worker thread reports new block of height {}", new_block_info.block.kernel.header.height); // Sanity check, remove for more efficient mining. // The below PoW check could fail due to race conditions. So we don't panic, @@ -415,7 +426,7 @@ pub async fn mine( // if it is not. assert!(new_block_info.block.is_valid(&latest_block), "Own mined block must be valid. Failed validity check after successful PoW check."); - info!("Found new {} block with block height {}. Hash: {}", global_state_lock.lock(|s| s.cli.network).await, new_block_info.block.header.height, new_block_info.block.hash.emojihash()); + info!("Found new {} block with block height {}. Hash: {}", global_state_lock.lock(|s| s.cli.network).await, new_block_info.block.kernel.header.height, new_block_info.block.hash().emojihash()); latest_block = *new_block_info.block.to_owned(); to_main.send(MinerToMain::NewBlockFound(new_block_info)).await?; @@ -484,6 +495,7 @@ mod mine_loop_tests { let block_template_empty_mempool = Block::new( block_header_template_empty_mempool, block_body_empty_mempool, + None, ); assert!( block_template_empty_mempool.is_valid(&genesis_block), @@ -531,7 +543,7 @@ mod mine_loop_tests { // Build and verify block template let (block_header_template, block_body) = make_block_template(&genesis_block, transaction_non_empty_mempool); - let block_template_non_empty_mempool = Block::new(block_header_template, block_body); + let block_template_non_empty_mempool = Block::new(block_header_template, block_body, None); assert!( block_template_non_empty_mempool.is_valid(&genesis_block), "Block template created by miner with non-empty mempool must be valid" diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index 76ef6fd84..72fd13737 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -1,17 +1,16 @@ use crate::prelude::twenty_first; +use get_size::GetSize; use serde::{Deserialize, Serialize}; -use twenty_first::shared_math::b_field_element::BFieldElement; use twenty_first::shared_math::bfield_codec::BFieldCodec; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::Transaction; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BFieldCodec)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BFieldCodec, GetSize)] pub struct BlockBody { pub transaction: Transaction, pub next_mutator_set_accumulator: MutatorSetAccumulator, pub previous_mutator_set_accumulator: MutatorSetAccumulator, - pub stark_proof: Vec, } diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 6930b243c..9468bad61 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -1,5 +1,6 @@ use crate::prelude::twenty_first; +use get_size::GetSize; use serde::{Deserialize, Serialize}; use std::fmt::Display; use twenty_first::shared_math::bfield_codec::BFieldCodec; @@ -15,7 +16,7 @@ pub const PROOF_OF_WORK_COUNT_U32_SIZE: usize = 5; pub const TARGET_BLOCK_INTERVAL: u64 = 588000; // 9.8 minutes in milliseconds pub const MINIMUM_DIFFICULTY: u32 = 2; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BFieldCodec)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, BFieldCodec, GetSize)] pub struct BlockHeader { pub version: BFieldElement, pub height: BlockHeight, diff --git a/src/models/blockchain/block/block_height.rs b/src/models/blockchain/block/block_height.rs index b655a174f..81ba5436d 100644 --- a/src/models/blockchain/block/block_height.rs +++ b/src/models/blockchain/block/block_height.rs @@ -1,5 +1,6 @@ use crate::prelude::twenty_first; +use get_size::GetSize; use num_traits::{One, Zero}; use serde::{Deserialize, Serialize}; use std::{ @@ -9,7 +10,9 @@ use std::{ }; use twenty_first::shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec}; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Hash, BFieldCodec)] +#[derive( + Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Hash, BFieldCodec, GetSize, +)] pub struct BlockHeight(BFieldElement); // Assuming a block time of 10 minutes, and a halving every three years, diff --git a/src/models/blockchain/block/block_kernel.rs b/src/models/blockchain/block/block_kernel.rs new file mode 100644 index 000000000..42e925af8 --- /dev/null +++ b/src/models/blockchain/block/block_kernel.rs @@ -0,0 +1,17 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::twenty_first::shared_math::{bfield_codec::BFieldCodec, tip5::Digest}; + +use super::{block_body::BlockBody, block_header::BlockHeader}; + +/// The kernel of a block contains all data that is not proof data +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BFieldCodec, GetSize)] +pub struct BlockKernel { + pub header: BlockHeader, + pub body: BlockBody, +} +impl BlockKernel { + pub(crate) fn mast_hash(&self) -> Digest { + todo!() + } +} diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 7cb217aa1..0eb006fa3 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -1,13 +1,16 @@ use crate::prelude::twenty_first; +use get_size::GetSize; use itertools::Itertools; use num_bigint::BigUint; use num_traits::{abs, Zero}; use serde::{Deserialize, Serialize}; use std::cmp::max; +use tasm_lib::triton_vm::proof::Proof; +use twenty_first::shared_math::bfield_codec::BFieldCodec; -use tracing::{debug, warn}; +use tracing::{debug, error, warn}; use twenty_first::amount::u32s::U32s; use twenty_first::shared_math::b_field_element::BFieldElement; @@ -18,14 +21,17 @@ use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; pub mod block_body; pub mod block_header; pub mod block_height; +pub mod block_kernel; pub mod mutator_set_update; pub mod transfer_block; +pub mod validity; use self::block_body::BlockBody; use self::block_header::{ BlockHeader, MINIMUM_DIFFICULTY, TARGET_BLOCK_INTERVAL, TARGET_DIFFICULTY_U32_SIZE, }; use self::block_height::BlockHeight; +use self::block_kernel::BlockKernel; use self::mutator_set_update::MutatorSetUpdate; use self::transfer_block::TransferBlock; use super::transaction::transaction_kernel::TransactionKernel; @@ -38,28 +44,42 @@ use crate::models::state::wallet::WalletSecret; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; use crate::util_types::mutator_set::mutator_set_trait::{commit, MutatorSet}; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BFieldCodec, GetSize)] pub struct Block { - pub hash: Digest, - pub header: BlockHeader, - pub body: BlockBody, + /// Everything but the proof + pub kernel: BlockKernel, + + /// All blocks have proofs except: + /// - the genesis block + /// - blocks being generated + pub proof: Option, } impl From for Block { fn from(t_block: TransferBlock) -> Self { Self { - hash: Hash::hash(&t_block.header), - header: t_block.header, - body: t_block.body, + kernel: BlockKernel { + header: t_block.header, + body: t_block.body, + }, + proof: Some(t_block.proof), } } } impl From for TransferBlock { fn from(block: Block) -> Self { + let proof = match block.proof { + Some(p) => p, + None => { + error!("In order to be transferred, a Block must have a non-None proof field."); + panic!() + } + }; Self { - header: block.header, - body: block.body, + header: block.kernel.header, + body: block.kernel.body, + proof, } } } @@ -67,24 +87,23 @@ impl From for TransferBlock { impl Block { #[inline] pub fn hash(&self) -> Digest { - self.hash + self.kernel.mast_hash() } #[inline] pub fn header(&self) -> &BlockHeader { - &self.header + &self.kernel.header } #[inline] pub fn body(&self) -> &BlockBody { - &self.body + &self.kernel.body } #[inline] pub fn set_block(&mut self, block: Block) { - self.hash = block.hash; - self.header = block.header; - self.body = block.body; + self.kernel.header = block.kernel.header; + self.kernel.body = block.kernel.body; } pub fn get_mining_reward(block_height: BlockHeight) -> Amount { @@ -147,7 +166,6 @@ impl Block { transaction: genesis_coinbase_tx, next_mutator_set_accumulator: genesis_mutator_set.clone(), previous_mutator_set_accumulator: empty_mutator_set, - stark_proof: vec![], }; let header: BlockHeader = BlockHeader { @@ -169,7 +187,7 @@ impl Block { uncles: vec![], }; - Self::new(header, body) + Self::new(header, body, None) } pub fn premine_distribution() -> Vec<(generation_address::ReceivingAddress, Amount)> { @@ -180,9 +198,11 @@ impl Block { vec![(authority_receiving_address, 20000.into())] } - pub fn new(header: BlockHeader, body: BlockBody) -> Self { - let hash = Hash::hash(&header); - Self { hash, header, body } + pub fn new(header: BlockHeader, body: BlockBody, proof: Option) -> Self { + Self { + kernel: BlockKernel { body, header }, + proof, + } } /// Merge a transaction into this block's transaction. @@ -190,18 +210,19 @@ impl Block { pub fn accumulate_transaction(&mut self, transaction: Transaction) { // merge let merged_timestamp = BFieldElement::new(max( - self.header.timestamp.value(), + self.kernel.header.timestamp.value(), max( - self.body.transaction.kernel.timestamp.value(), + self.kernel.body.transaction.kernel.timestamp.value(), transaction.kernel.timestamp.value(), ), )); - let new_transaction = self.body.transaction.clone().merge_with(transaction); + let new_transaction = self.kernel.body.transaction.clone().merge_with(transaction); // accumulate let additions = new_transaction.kernel.outputs.clone(); let removals = new_transaction.kernel.inputs.clone(); - let mut next_mutator_set_accumulator = self.body.previous_mutator_set_accumulator.clone(); + let mut next_mutator_set_accumulator = + self.kernel.body.previous_mutator_set_accumulator.clone(); let mutator_set_update = MutatorSetUpdate::new(removals, additions); @@ -213,28 +234,30 @@ impl Block { let block_body: BlockBody = BlockBody { transaction: new_transaction, next_mutator_set_accumulator: next_mutator_set_accumulator.clone(), - previous_mutator_set_accumulator: self.body.previous_mutator_set_accumulator.clone(), - stark_proof: vec![], + previous_mutator_set_accumulator: self + .kernel + .body + .previous_mutator_set_accumulator + .clone(), }; let block_header = BlockHeader { - version: self.header.version, - height: self.header.height, + version: self.kernel.header.version, + height: self.kernel.header.height, mutator_set_hash: next_mutator_set_accumulator.hash(), - prev_block_digest: self.header.prev_block_digest, + prev_block_digest: self.kernel.header.prev_block_digest, timestamp: merged_timestamp, - nonce: self.header.nonce, - max_block_size: self.header.max_block_size, - proof_of_work_line: self.header.proof_of_work_line, - proof_of_work_family: self.header.proof_of_work_family, - difficulty: self.header.difficulty, + nonce: self.kernel.header.nonce, + max_block_size: self.kernel.header.max_block_size, + proof_of_work_line: self.kernel.header.proof_of_work_line, + proof_of_work_family: self.kernel.header.proof_of_work_family, + difficulty: self.kernel.header.difficulty, block_body_merkle_root: Hash::hash(&block_body), uncles: vec![], }; - self.body = block_body; - self.hash = Hash::hash(&block_header); - self.header = block_header; + self.kernel.body = block_body; + self.kernel.header = block_header; } /// Verify a block. It is assumed that `previous_block` is valid. @@ -265,34 +288,36 @@ impl Block { // g) transaction is valid (internally consistent) // 0.a) Block height is previous plus one - if previous_block.header.height.next() != block_copy.header.height { + if previous_block.kernel.header.height.next() != block_copy.kernel.header.height { warn!("Height does not match previous height"); return false; } // 0.b) Block header points to previous block - if previous_block.hash != block_copy.header.prev_block_digest { + if previous_block.hash() != block_copy.kernel.header.prev_block_digest { warn!("Hash digest does not match previous digest"); return false; } // 0.c) Block timestamp is greater than that of previuos block - if previous_block.header.timestamp.value() >= block_copy.header.timestamp.value() { + if previous_block.kernel.header.timestamp.value() + >= block_copy.kernel.header.timestamp.value() + { warn!("Block does not have greater timestamp than that of previous block"); return false; } // 0.d) Next mutator set of previous block matches previous MS of current block - if previous_block.body.next_mutator_set_accumulator - != block_copy.body.previous_mutator_set_accumulator + if previous_block.kernel.body.next_mutator_set_accumulator + != block_copy.kernel.body.previous_mutator_set_accumulator { warn!("Value for previous mutator set does not match previous block"); return false; } // 0.e) Target difficulty was updated correctly - if block_copy.header.difficulty - != Self::difficulty_control(previous_block, block_copy.header.timestamp.value()) + if block_copy.kernel.header.difficulty + != Self::difficulty_control(previous_block, block_copy.kernel.header.timestamp.value()) { warn!("Value for new difficulty is incorrect."); return false; @@ -300,8 +325,9 @@ impl Block { // 1.b) Verify validity of removal records: That their MMR MPs match the SWBF, and // that at least one of their listed indices is absent. - for removal_record in block_copy.body.transaction.kernel.inputs.iter() { + for removal_record in block_copy.kernel.body.transaction.kernel.inputs.iter() { if !block_copy + .kernel .body .previous_mutator_set_accumulator .kernel @@ -314,6 +340,7 @@ impl Block { // 1.c) Verify that the removal records do not contain duplicate `AbsoluteIndexSet`s let mut absolute_index_sets = block_copy + .kernel .body .transaction .kernel @@ -323,7 +350,7 @@ impl Block { .collect_vec(); absolute_index_sets.sort(); absolute_index_sets.dedup(); - if absolute_index_sets.len() != block_copy.body.transaction.kernel.inputs.len() { + if absolute_index_sets.len() != block_copy.kernel.body.transaction.kernel.inputs.len() { warn!("Removal records contain duplicates"); return false; } @@ -333,10 +360,14 @@ impl Block { // Construct all the addition records for all the transaction outputs. Then // use these addition records to insert into the mutator set. let mutator_set_update = MutatorSetUpdate::new( - block_copy.body.transaction.kernel.inputs.clone(), - block_copy.body.transaction.kernel.outputs.clone(), + block_copy.kernel.body.transaction.kernel.inputs.clone(), + block_copy.kernel.body.transaction.kernel.outputs.clone(), ); - let mut ms = block_copy.body.previous_mutator_set_accumulator.clone(); + let mut ms = block_copy + .kernel + .body + .previous_mutator_set_accumulator + .clone(); let ms_update_result = mutator_set_update.apply(&mut ms); match ms_update_result { Ok(()) => (), @@ -348,24 +379,24 @@ impl Block { // Verify that the locally constructed mutator set matches that in the received // block's body. - if ms.hash() != block_copy.body.next_mutator_set_accumulator.hash() { + if ms.hash() != block_copy.kernel.body.next_mutator_set_accumulator.hash() { warn!("Reported mutator set does not match calculated object."); debug!( "From Block\n{:?}. \n\n\nCalculated\n{:?}", - block_copy.body.next_mutator_set_accumulator, ms + block_copy.kernel.body.next_mutator_set_accumulator, ms ); return false; } // Verify that the locally constructed mutator set matches that in the received block's header - if ms.hash() != block_copy.header.mutator_set_hash { + if ms.hash() != block_copy.kernel.header.mutator_set_hash { warn!("Mutator set commitment does not match calculated object"); return false; } // 1.e) verify that the transaction timestamp is less than or equal to the block's timestamp. - if block_copy.body.transaction.kernel.timestamp.value() - > block_copy.header.timestamp.value() + if block_copy.kernel.body.transaction.kernel.timestamp.value() + > block_copy.kernel.header.timestamp.value() { warn!("Transaction with invalid timestamp found"); return false; @@ -373,9 +404,9 @@ impl Block { // 1.f) Verify that the coinbase claimed by the transaction does not exceed // the allowed coinbase based on block height, epoch, etc., and fee - let miner_reward: Amount = - Self::get_mining_reward(block_copy.header.height) + self.body.transaction.kernel.fee; - if let Some(claimed_reward) = block_copy.body.transaction.kernel.coinbase { + let miner_reward: Amount = Self::get_mining_reward(block_copy.kernel.header.height) + + self.kernel.body.transaction.kernel.fee; + if let Some(claimed_reward) = block_copy.kernel.body.transaction.kernel.coinbase { if claimed_reward > miner_reward { warn!("Block is invalid because the claimed miner reward is too high relative to current network parameters."); return false; @@ -383,7 +414,7 @@ impl Block { } // 1.g) Verify transaction, but without relating it to the blockchain tip (that was done above). - if !block_copy.body.transaction.is_valid() { + if !block_copy.kernel.body.transaction.is_valid() { warn!("Invalid transaction found in block"); return false; } @@ -405,7 +436,7 @@ impl Block { // 4.2. verify that all uncles' hash are below parent's target_difficulty // 5. `block_body_merkle_root` - if block_copy.header.block_body_merkle_root != Hash::hash(&block_copy.body) { + if block_copy.kernel.header.block_body_merkle_root != Hash::hash(&block_copy.kernel.body) { warn!("Block body does not match referenced block body Merkle root"); return false; } @@ -418,7 +449,9 @@ impl Block { /// the previous. pub fn has_proof_of_work(&self, previous_block: &Block) -> bool { // check that hash is below threshold - if self.hash > Self::difficulty_to_digest_threshold(previous_block.header.difficulty) { + if self.hash() + > Self::difficulty_to_digest_threshold(previous_block.kernel.header.difficulty) + { warn!("Block digest exceeds target difficulty"); return false; } @@ -449,12 +482,12 @@ impl Block { new_timestamp: u64, ) -> U32s { // no adjustment if the previous block is the genesis block - if old_block.header.height.is_genesis() { - return old_block.header.difficulty; + if old_block.kernel.header.height.is_genesis() { + return old_block.kernel.header.difficulty; } // otherwise, compute PID control signal - let t = new_timestamp - old_block.header.timestamp.value(); + let t = new_timestamp - old_block.kernel.header.timestamp.value(); let new_error = t as i64 - TARGET_BLOCK_INTERVAL as i64; @@ -466,11 +499,11 @@ impl Block { let adjustment_u32s = U32s::::new([adj_lo, adj_hi, 0u32, 0u32, 0u32]); if adjustment_is_positive { - old_block.header.difficulty + adjustment_u32s - } else if adjustment_u32s > old_block.header.difficulty - MINIMUM_DIFFICULTY.into() { + old_block.kernel.header.difficulty + adjustment_u32s + } else if adjustment_u32s > old_block.kernel.header.difficulty - MINIMUM_DIFFICULTY.into() { MINIMUM_DIFFICULTY.into() } else { - old_block.header.difficulty - adjustment_u32s + old_block.kernel.header.difficulty - adjustment_u32s } } } @@ -538,12 +571,12 @@ mod block_tests { assert!( block_1.is_valid(&genesis_block), "Block 1 must be valid after adding a transaction; previous mutator set hash: {} and next mutator set hash: {}", - block_1 + block_1.kernel .body .previous_mutator_set_accumulator .hash() .emojihash(), - block_1 + block_1.kernel .body .next_mutator_set_accumulator .hash() @@ -553,12 +586,12 @@ mod block_tests { // Sanity checks assert_eq!( 3, - block_1.body.transaction.kernel.outputs.len(), + block_1.kernel.body.transaction.kernel.outputs.len(), "New block must have three outputs: coinbase, transaction, and change" ); assert_eq!( 1, - block_1.body.transaction.kernel.inputs.len(), + block_1.kernel.body.transaction.kernel.inputs.len(), "New block must have one input: spending of genesis UTXO" ); } diff --git a/src/models/blockchain/block/transfer_block.rs b/src/models/blockchain/block/transfer_block.rs index 747df8395..4e8a7028e 100644 --- a/src/models/blockchain/block/transfer_block.rs +++ b/src/models/blockchain/block/transfer_block.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use tasm_lib::triton_vm::proof::Proof; use super::{block_body::BlockBody, block_header::BlockHeader}; @@ -8,4 +9,5 @@ use super::{block_body::BlockBody, block_header::BlockHeader}; pub struct TransferBlock { pub header: BlockHeader, pub body: BlockBody, + pub proof: Proof, } diff --git a/src/models/blockchain/block/validity.rs b/src/models/blockchain/block/validity.rs new file mode 100644 index 000000000..083ea6b62 --- /dev/null +++ b/src/models/blockchain/block/validity.rs @@ -0,0 +1,41 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::twenty_first; +use twenty_first::shared_math::bfield_codec::BFieldCodec; + +use self::{ + coinbase_is_valid::CoinbaseIsValid, + correct_control_parameter_update::CorrectControlParameterUpdate, + correct_mmr_update::CorrectMmrUpdate, correct_mutator_set_update::CorrectMutatorSetUpdate, + predecessor_is_valid::PredecessorIsValid, transaction_is_valid::TransactionIsValid, +}; + +pub mod coinbase_is_valid; +pub mod correct_control_parameter_update; +pub mod correct_mmr_update; +pub mod correct_mutator_set_update; +pub mod predecessor_is_valid; +pub mod transaction_is_valid; + +/// The validity of a block, when it is not the genesis block and when it does not +/// come with proofs, decomposes into these subclaims. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] +pub struct BlockValidationLogic { + // program: recursive-verify-or-is-genesis, input: block kernel, output: [] + pub predecessor_is_valid: PredecessorIsValid, + + // program: verify-transaction, input: block kernel, output: [] + pub transaction_is_valid: TransactionIsValid, + + // program: verify-coinbase, input: block kernel, output: [] + pub coinbase_is_valid: CoinbaseIsValid, + + // program: update-mutator-set, input: block kernel, output: [] + pub correct_mutator_set_update: CorrectMutatorSetUpdate, + + // program: update-mmr, input: block kernel, output: [] + pub correct_mmr_update: CorrectMmrUpdate, + + // program: update-control-parameters, input: block kernel, output: [] + pub correct_control_parameter_update: CorrectControlParameterUpdate, +} diff --git a/src/models/blockchain/block/validity/coinbase_is_valid.rs b/src/models/blockchain/block/validity/coinbase_is_valid.rs new file mode 100644 index 000000000..41be8d920 --- /dev/null +++ b/src/models/blockchain/block/validity/coinbase_is_valid.rs @@ -0,0 +1,32 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::{self, shared_math::b_field_element::BFieldElement}, +}; +use twenty_first::shared_math::bfield_codec::BFieldCodec; + +use crate::models::{ + blockchain::block::Block, + consensus::{SecretWitness, SupportedClaim}, +}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CoinbaseIsValidWitness { + pub block: Block, +} + +impl SecretWitness for CoinbaseIsValidWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CoinbaseIsValid { + supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/block/validity/correct_control_parameter_update.rs b/src/models/blockchain/block/validity/correct_control_parameter_update.rs new file mode 100644 index 000000000..a8904e6f7 --- /dev/null +++ b/src/models/blockchain/block/validity/correct_control_parameter_update.rs @@ -0,0 +1,31 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec}, +}; + +use crate::models::{ + blockchain::block::Block, + consensus::{SecretWitness, SupportedClaim}, +}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectControlParameterUpdateWitness { + pub previous_block: Block, +} + +impl SecretWitness for CorrectControlParameterUpdateWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectControlParameterUpdate { + pub supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/block/validity/correct_mmr_update.rs b/src/models/blockchain/block/validity/correct_mmr_update.rs new file mode 100644 index 000000000..557382960 --- /dev/null +++ b/src/models/blockchain/block/validity/correct_mmr_update.rs @@ -0,0 +1,32 @@ +use crate::Hash; +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::{ + shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec}, + util_types::mmr::mmr_accumulator::MmrAccumulator, + }, +}; + +use crate::models::consensus::{SecretWitness, SupportedClaim}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectMmrUpdateWitness { + pub previous_mmr_accumulator: MmrAccumulator, +} + +impl SecretWitness for CorrectMmrUpdateWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectMmrUpdate { + pub supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/block/validity/correct_mutator_set_update.rs b/src/models/blockchain/block/validity/correct_mutator_set_update.rs new file mode 100644 index 000000000..553dd5ed7 --- /dev/null +++ b/src/models/blockchain/block/validity/correct_mutator_set_update.rs @@ -0,0 +1,34 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::{bfieldcodec_derive::BFieldCodec, shared_math::b_field_element::BFieldElement}, +}; + +use crate::{ + models::{ + blockchain::shared::Hash, + consensus::{SecretWitness, SupportedClaim}, + }, + util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator, +}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectMutatorSetUpdateWitness { + previous_mutator_set_accumulator: MutatorSetAccumulator, +} + +impl SecretWitness for CorrectMutatorSetUpdateWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct CorrectMutatorSetUpdate { + pub supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/block/validity/predecessor_is_valid.rs b/src/models/blockchain/block/validity/predecessor_is_valid.rs new file mode 100644 index 000000000..79481ff7a --- /dev/null +++ b/src/models/blockchain/block/validity/predecessor_is_valid.rs @@ -0,0 +1,31 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec}, +}; + +use crate::models::{ + blockchain::block::Block, + consensus::{SecretWitness, SupportedClaim}, +}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct PredecessorIsValidWitness { + pub predecessor_block: Block, +} + +impl SecretWitness for PredecessorIsValidWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct PredecessorIsValid { + pub supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/block/validity/transaction_is_valid.rs b/src/models/blockchain/block/validity/transaction_is_valid.rs new file mode 100644 index 000000000..4453588c9 --- /dev/null +++ b/src/models/blockchain/block/validity/transaction_is_valid.rs @@ -0,0 +1,31 @@ +use get_size::GetSize; +use serde::{Deserialize, Serialize}; +use tasm_lib::{ + triton_vm::program::{NonDeterminism, Program}, + twenty_first::shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec}, +}; + +use crate::models::{ + blockchain::transaction::Transaction, + consensus::{SecretWitness, SupportedClaim}, +}; + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransactionIsValidWitness { + transaction: Transaction, +} + +impl SecretWitness for TransactionIsValidWitness { + fn nondeterminism(&self) -> NonDeterminism { + todo!() + } + + fn subprogram(&self) -> Program { + todo!() + } +} + +#[derive(Debug, Clone, BFieldCodec, GetSize, PartialEq, Eq, Serialize, Deserialize)] +pub struct TransactionIsValid { + supported_claim: SupportedClaim, +} diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index 8c0f2a4e9..73aed9852 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -87,14 +87,17 @@ impl Transaction { /// invalidate the proof, requiring an update. For LinkedProofs or PrimitiveWitness /// witnesses the witness data can be and is updated. pub fn update_mutator_set_records(&mut self, block: &Block) -> Result<()> { - let mut msa_state: MutatorSetAccumulator = - block.body.previous_mutator_set_accumulator.to_owned(); + let mut msa_state: MutatorSetAccumulator = block + .kernel + .body + .previous_mutator_set_accumulator + .to_owned(); let block_addition_records: Vec = - block.body.transaction.kernel.outputs.clone(); + block.kernel.body.transaction.kernel.outputs.clone(); let mut transaction_removal_records: Vec> = self.kernel.inputs.clone(); let mut transaction_removal_records: Vec<&mut RemovalRecord> = transaction_removal_records.iter_mut().collect(); - let mut block_removal_records = block.body.transaction.kernel.inputs.clone(); + let mut block_removal_records = block.kernel.body.transaction.kernel.inputs.clone(); block_removal_records.reverse(); let mut block_removal_records: Vec<&mut RemovalRecord> = block_removal_records.iter_mut().collect::>(); @@ -157,7 +160,12 @@ impl Transaction { } // Sanity check of block validity - let block_msa_hash = block.body.next_mutator_set_accumulator.clone().hash(); + let block_msa_hash = block + .kernel + .body + .next_mutator_set_accumulator + .clone() + .hash(); assert_eq!( msa_state.hash(), block_msa_hash, diff --git a/src/models/blockchain/transaction/validity.rs b/src/models/blockchain/transaction/validity.rs index 8645abc22..52621e073 100644 --- a/src/models/blockchain/transaction/validity.rs +++ b/src/models/blockchain/transaction/validity.rs @@ -70,24 +70,23 @@ impl SupportedClaim { } } -/// ValidityConditions is a helper struct. It contains a sequence of -/// claims with optional witnesses. If all claims a true, then the -/// transaction is valid. +/// The validity of a transaction, in the base case, decomposes into +/// these subclaims. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] pub struct TransactionValidationLogic { - // programs: [lock_script], input: hash of tx kernel (MAST hash), witness: secret spending key, output: [] + // programs: [lock_script], input: transaction kernel mast hash, witness: secret spending key, output: [] pub lock_scripts_halt: LockScriptsHalt, - // program: todo, input: hash of tx kernel (MAST hash), witness: input utxos, utxo mast auth path, output: hashes of lock scripts + // program: todo, input: transaction kernel mast hash, witness: input utxos, utxo mast auth path, output: hashes of lock scripts pub kernel_to_lock_scripts: KernelToLockScripts, - // program: recompute swbf indices, input: hash of kernel, witness: inputs + mutator set accumulator, output: [] + // program: recompute swbf indices, input: transaction kernel mast hash, witness: inputs + mutator set accumulator, output: [] pub removal_records_integrity: RemovalRecordsIntegrity, - // program: todo, input: hash of tx kernel (MAST hash), witness: outputs + kernel mast auth path + coins, output: type scripts + // program: todo, input: transaction kernel mast hash, witness: outputs + kernel mast auth path + coins, output: hashes of type scripts pub kernel_to_typescripts: KernelToTypeScripts, - // program: type script, input: hash of inputs + hash of outputs + coinbase + fee, witness: inputs + outputs + any, output: [] + // programs: [type script], input: transaction kernel mast hash, witness: inputs + outputs + any, output: [] pub type_scripts_halt: TypeScriptsHalt, } diff --git a/src/models/peer.rs b/src/models/peer.rs index 24fa13438..bb5c81a0c 100644 --- a/src/models/peer.rs +++ b/src/models/peer.rs @@ -211,9 +211,9 @@ pub struct PeerBlockNotification { impl From<&Block> for PeerBlockNotification { fn from(block: &Block) -> Self { PeerBlockNotification { - hash: block.hash, - height: block.header.height, - proof_of_work_family: block.header.proof_of_work_family, + hash: block.hash(), + height: block.kernel.header.height, + proof_of_work_family: block.kernel.header.proof_of_work_family, } } } @@ -221,9 +221,9 @@ impl From<&Block> for PeerBlockNotification { impl From for PeerBlockNotification { fn from(block: Block) -> Self { PeerBlockNotification { - hash: block.hash, - height: block.header.height, - proof_of_work_family: block.header.proof_of_work_family, + hash: block.hash(), + height: block.kernel.header.height, + proof_of_work_family: block.kernel.header.proof_of_work_family, } } } diff --git a/src/models/shared.rs b/src/models/shared.rs index d9736639b..af4ad5f30 100644 --- a/src/models/shared.rs +++ b/src/models/shared.rs @@ -16,8 +16,8 @@ pub struct LatestBlockInfo { impl From<&Block> for LatestBlockInfo { fn from(b: &Block) -> Self { Self { - hash: b.hash, - height: b.header.height, + hash: b.hash(), + height: b.kernel.header.height, } } } @@ -25,8 +25,8 @@ impl From<&Block> for LatestBlockInfo { impl From for LatestBlockInfo { fn from(b: Block) -> Self { Self { - hash: b.hash, - height: b.header.height, + hash: b.hash(), + height: b.kernel.header.height, } } } diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 4f247984d..16d25bf6b 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -200,10 +200,10 @@ impl ArchivalState { // the setup, but we don't have the genesis block in scope before this function, so it makes // sense to do it here. if archival_mutator_set.ams().kernel.aocl.is_empty() { - for addition_record in genesis_block.body.transaction.kernel.outputs.iter() { + for addition_record in genesis_block.kernel.body.transaction.kernel.outputs.iter() { archival_mutator_set.ams_mut().add(addition_record); } - archival_mutator_set.set_sync_label(genesis_block.hash); + archival_mutator_set.set_sync_label(genesis_block.hash()); archival_mutator_set.persist(); } @@ -261,13 +261,13 @@ impl ArchivalState { .await .map(|x| x.as_file_record()); let file_record_value: FileRecord = match file_record_value { - Some(record) => record.add(serialized_block_size, &new_block.header), + Some(record) => record.add(serialized_block_size, &new_block.kernel.header), None => { assert!( block_file.metadata().unwrap().len().is_zero(), "If no file record exists, block file must be empty" ); - FileRecord::new(serialized_block_size, &new_block.header) + FileRecord::new(serialized_block_size, &new_block.kernel.header) } }; @@ -287,7 +287,7 @@ impl ArchivalState { block_file.metadata().unwrap().len() ); - let height_record_key = BlockIndexKey::Height(new_block.header.height); + let height_record_key = BlockIndexKey::Height(new_block.kernel.header.height); let mut blocks_at_same_height: Vec = match self.block_index_db.get(height_record_key.clone()).await { Some(rec) => rec.as_height_record(), @@ -306,9 +306,9 @@ impl ArchivalState { // Update block index database with newly stored block let mut block_index_entries: Vec<(BlockIndexKey, BlockIndexValue)> = vec![]; - let block_record_key: BlockIndexKey = BlockIndexKey::Block(new_block.hash); + let block_record_key: BlockIndexKey = BlockIndexKey::Block(new_block.hash()); let block_record_value: BlockIndexValue = BlockIndexValue::Block(Box::new(BlockRecord { - block_header: new_block.header.clone(), + block_header: new_block.kernel.header.clone(), file_location: BlockFileLocation { file_index: last_rec.last_file, offset: file_offset, @@ -320,7 +320,7 @@ impl ArchivalState { block_index_entries.push((block_record_key, block_record_value)); block_index_entries.push((BlockIndexKey::LastFile, BlockIndexValue::LastFile(last_rec))); - blocks_at_same_height.push(new_block.hash); + blocks_at_same_height.push(new_block.hash()); block_index_entries.push(( height_record_key, BlockIndexValue::Height(blocks_at_same_height), @@ -328,11 +328,11 @@ impl ArchivalState { // Mark block as tip if its PoW family is larger than current most canonical if current_max_pow_family.is_none() - || current_max_pow_family.unwrap() < new_block.header.proof_of_work_family + || current_max_pow_family.unwrap() < new_block.kernel.header.proof_of_work_family { block_index_entries.push(( BlockIndexKey::BlockTipDigest, - BlockIndexValue::BlockTipDigest(new_block.hash), + BlockIndexValue::BlockTipDigest(new_block.hash()), )); } @@ -408,8 +408,8 @@ impl ArchivalState { .map(|x| x.as_block_record().block_header); // If no block was found, check if digest is genesis digest - if ret.is_none() && block_digest == self.genesis_block.hash { - ret = Some(self.genesis_block.header.clone()); + if ret.is_none() && block_digest == self.genesis_block.hash() { + ret = Some(self.genesis_block.kernel.header.clone()); } ret @@ -425,7 +425,7 @@ impl ArchivalState { let record: BlockRecord = match maybe_record { Some(rec) => rec, None => { - if self.genesis_block.hash == block_digest { + if self.genesis_block.hash() == block_digest { return Ok(Some(*self.genesis_block.clone())); } else { return Ok(None); @@ -515,7 +515,7 @@ impl ArchivalState { } // If block is genesis block, it belongs to the canonical chain - if block_header_digest == self.genesis_block.hash { + if block_header_digest == self.genesis_block.hash() { return true; } @@ -579,15 +579,18 @@ impl ArchivalState { // Find path from mutator set sync digest to new block. Optimize for the common case, // where the new block is the child block of block that the mutator set is synced to. let (backwards, _luca, forwards) = - if ms_block_sync_digest == new_block.header.prev_block_digest { + if ms_block_sync_digest == new_block.kernel.header.prev_block_digest { // Trivial path (vec![], ms_block_sync_digest, vec![]) } else { // Non-trivial path from current mutator set sync digest to new block - self.find_path(ms_block_sync_digest, new_block.header.prev_block_digest) - .await + self.find_path( + ms_block_sync_digest, + new_block.kernel.header.prev_block_digest, + ) + .await }; - let forwards = [forwards, vec![new_block.hash]].concat(); + let forwards = [forwards, vec![new_block.hash()]].concat(); (forwards, backwards) }; @@ -602,11 +605,19 @@ impl ArchivalState { debug!( "Updating mutator set: rolling back block with height {}", - roll_back_block.header.height + roll_back_block.kernel.header.height ); // Roll back all addition records contained in block - for addition_record in roll_back_block.body.transaction.kernel.outputs.iter().rev() { + for addition_record in roll_back_block + .kernel + .body + .transaction + .kernel + .outputs + .iter() + .rev() + { assert!( self.archival_mutator_set .ams_mut() @@ -619,7 +630,7 @@ impl ArchivalState { } // Roll back all removal records contained in block - for removal_record in roll_back_block.body.transaction.kernel.inputs.iter() { + for removal_record in roll_back_block.kernel.body.transaction.kernel.inputs.iter() { self.archival_mutator_set .ams_mut() .revert_remove(removal_record); @@ -628,7 +639,7 @@ impl ArchivalState { for digest in forwards { // Add block to mutator set - let apply_forward_block = if digest == new_block.hash { + let apply_forward_block = if digest == new_block.hash() { new_block.to_owned() } else { self.get_block(digest) @@ -638,15 +649,28 @@ impl ArchivalState { }; debug!( "Updating mutator set: adding block with height {}. Mined: {}", - apply_forward_block.header.height, - crate::utc_timestamp_to_localtime(apply_forward_block.header.timestamp.value()) - .to_string() + apply_forward_block.kernel.header.height, + crate::utc_timestamp_to_localtime( + apply_forward_block.kernel.header.timestamp.value() + ) + .to_string() ); - let mut addition_records: Vec = - apply_forward_block.body.transaction.kernel.outputs.clone(); + let mut addition_records: Vec = apply_forward_block + .kernel + .body + .transaction + .kernel + .outputs + .clone(); addition_records.reverse(); - let mut removal_records = apply_forward_block.body.transaction.kernel.inputs.clone(); + let mut removal_records = apply_forward_block + .kernel + .body + .transaction + .kernel + .inputs + .clone(); removal_records.reverse(); let mut removal_records: Vec<&mut RemovalRecord> = removal_records.iter_mut().collect::>(); @@ -680,15 +704,15 @@ impl ArchivalState { debug!("sanity check: was AMS updated consistently with new block?"); assert_eq!( new_block - .body + .kernel.body .next_mutator_set_accumulator .hash(), self.archival_mutator_set.ams().hash(), - "Calculated archival mutator set commitment must match that from newly added block. Block Digest: {:?}", new_block.hash + "Calculated archival mutator set commitment must match that from newly added block. Block Digest: {:?}", new_block.hash() ); // Persist updated mutator set to disk, with sync label - self.archival_mutator_set.set_sync_label(new_block.hash); + self.archival_mutator_set.set_sync_label(new_block.hash()); self.archival_mutator_set.persist(); Ok(()) @@ -744,7 +768,7 @@ mod archival_state_tests { .await .unwrap(); let _c = archival_state0 - .get_block(block_1.hash) + .get_block(block_1.hash()) .await .unwrap() .unwrap(); @@ -761,7 +785,13 @@ mod archival_state_tests { let archival_state = make_test_archival_state(Network::Alpha).await; assert_eq!( - Block::genesis_block().body.transaction.kernel.outputs.len() as u64, + Block::genesis_block() + .kernel + .body + .transaction + .kernel + .outputs + .len() as u64, archival_state .archival_mutator_set .ams() @@ -772,7 +802,7 @@ mod archival_state_tests { ); assert_eq!( - Block::genesis_block().hash, + Block::genesis_block().hash(), archival_state.archival_mutator_set.get_sync_label(), "AMS must be synced to genesis block after initialization from genesis block" ); @@ -804,7 +834,7 @@ mod archival_state_tests { let restored_archival_state = archival_state; assert_eq!( - mock_block_1.hash, + mock_block_1.hash(), restored_archival_state .archival_mutator_set .get_sync_label(), @@ -917,7 +947,7 @@ mod archival_state_tests { archival_state .write_block( &mock_block_1a, - Some(mock_block_1a.header.proof_of_work_family), + Some(mock_block_1a.kernel.header.proof_of_work_family), ) .await?; @@ -936,7 +966,7 @@ mod archival_state_tests { archival_state .write_block( &mock_block_1a, - Some(mock_block_1b.header.proof_of_work_family), + Some(mock_block_1b.kernel.header.proof_of_work_family), ) .await?; @@ -1008,7 +1038,7 @@ mod archival_state_tests { { archival_state - .write_block(&block_1a, Some(block_1a.header.proof_of_work_family)) + .write_block(&block_1a, Some(block_1a.kernel.header.proof_of_work_family)) .await .unwrap(); @@ -1022,7 +1052,10 @@ mod archival_state_tests { own_receiving_address, ); archival_state - .write_block(&block_1a, Some(mock_block_1b.header.proof_of_work_family)) + .write_block( + &block_1a, + Some(mock_block_1b.kernel.header.proof_of_work_family), + ) .await .unwrap(); @@ -1123,7 +1156,10 @@ mod archival_state_tests { global_state .chain .archival_state_mut() - .write_block(&next_block, Some(next_block.header.proof_of_work_family)) + .write_block( + &next_block, + Some(next_block.kernel.header.proof_of_work_family), + ) .await?; global_state .chain @@ -1149,10 +1185,10 @@ mod archival_state_tests { // Genesis block may have a different number of outputs than the blocks produced above if i == 0 { _aocl_index_of_consumed_input += - genesis_block.body.transaction.kernel.outputs.len() as u64; + genesis_block.kernel.body.transaction.kernel.outputs.len() as u64; } else { _aocl_index_of_consumed_input += - next_block.body.transaction.kernel.outputs.len() as u64; + next_block.kernel.body.transaction.kernel.outputs.len() as u64; } previous_block = next_block; @@ -1167,7 +1203,7 @@ mod archival_state_tests { .archival_state_mut() .write_block( &mock_block_1b, - Some(mock_block_1b.header.proof_of_work_family), + Some(mock_block_1b.kernel.header.proof_of_work_family), ) .await?; @@ -1519,8 +1555,8 @@ mod archival_state_tests { block_2.accumulate_transaction(tx_from_bob); // Sanity checks - assert_eq!(4, block_2.body.transaction.kernel.inputs.len()); - assert_eq!(6, block_2.body.transaction.kernel.outputs.len()); + assert_eq!(4, block_2.kernel.body.transaction.kernel.inputs.len()); + assert_eq!(6, block_2.kernel.body.transaction.kernel.outputs.len()); assert!(block_2.is_valid(&block_1)); // Update chain states @@ -1630,7 +1666,7 @@ mod archival_state_tests { let state = state_lock.lock_guard().await; assert_eq!( - block_2.body.next_mutator_set_accumulator, + block_2.kernel.body.next_mutator_set_accumulator, state .chain .archival_state() @@ -1709,14 +1745,14 @@ mod archival_state_tests { make_mock_block_with_valid_pow(&genesis.clone(), None, own_receiving_address); // Lookup a block in an empty database, expect None to be returned - let ret0 = archival_state.get_block(mock_block_1.hash).await?; + let ret0 = archival_state.get_block(mock_block_1.hash()).await?; assert!( ret0.is_none(), "Must return a block when one is stored to DB" ); add_block_to_archival_state(&mut archival_state, mock_block_1.clone()).await?; - let ret1 = archival_state.get_block(mock_block_1.hash).await?; + let ret1 = archival_state.get_block(mock_block_1.hash()).await?; assert!( ret1.is_some(), "Must return a block when one is stored to DB" @@ -1731,12 +1767,18 @@ mod archival_state_tests { let (mock_block_2, _, _) = make_mock_block_with_valid_pow(&mock_block_1.clone(), None, own_receiving_address); add_block_to_archival_state(&mut archival_state, mock_block_2.clone()).await?; - let fetched2 = archival_state.get_block(mock_block_2.hash).await?.unwrap(); + let fetched2 = archival_state + .get_block(mock_block_2.hash()) + .await? + .unwrap(); assert_eq!( mock_block_2, fetched2, "Returned block must match the one inserted" ); - let fetched1 = archival_state.get_block(mock_block_1.hash).await?.unwrap(); + let fetched1 = archival_state + .get_block(mock_block_1.hash()) + .await? + .unwrap(); assert_eq!( mock_block_1, fetched1, "Returned block must match the one inserted" @@ -1754,7 +1796,10 @@ mod archival_state_tests { } for block in blocks { - assert_eq!(block, archival_state.get_block(block.hash).await?.unwrap()); + assert_eq!( + block, + archival_state.get_block(block.hash()).await?.unwrap() + ); } Ok(()) @@ -1768,8 +1813,9 @@ mod archival_state_tests { let genesis = *archival_state.genesis_block.clone(); // Test that `find_path` returns the correct result - let (backwards_0, luca_0, forwards_0) = - archival_state.find_path(genesis.hash, genesis.hash).await; + let (backwards_0, luca_0, forwards_0) = archival_state + .find_path(genesis.hash(), genesis.hash()) + .await; assert!( backwards_0.is_empty(), "Backwards path from genesis to genesis is empty" @@ -1779,7 +1825,8 @@ mod archival_state_tests { "Forward path from genesis to genesis is empty" ); assert_eq!( - genesis.hash, luca_0, + genesis.hash(), + luca_0, "Luca of genesis and genesis is genesis" ); @@ -1796,49 +1843,49 @@ mod archival_state_tests { // Test 1a let (backwards_1, luca_1, forwards_1) = archival_state - .find_path(genesis.hash, mock_block_1_a.hash) + .find_path(genesis.hash(), mock_block_1_a.hash()) .await; assert!( backwards_1.is_empty(), "Backwards path from genesis to 1a is empty" ); assert_eq!( - vec![mock_block_1_a.hash], + vec![mock_block_1_a.hash()], forwards_1, "Forwards from genesis to block 1a is block 1a" ); - assert_eq!(genesis.hash, luca_1, "Luca of genesis and 1a is genesis"); + assert_eq!(genesis.hash(), luca_1, "Luca of genesis and 1a is genesis"); // Test 1b let (backwards_2, luca_2, forwards_2) = archival_state - .find_path(genesis.hash, mock_block_1_b.hash) + .find_path(genesis.hash(), mock_block_1_b.hash()) .await; assert!( backwards_2.is_empty(), "Backwards path from genesis to 1b is empty" ); assert_eq!( - vec![mock_block_1_b.hash], + vec![mock_block_1_b.hash()], forwards_2, "Forwards from genesis to block 1b is block 1a" ); - assert_eq!(genesis.hash, luca_2, "Luca of genesis and 1b is genesis"); + assert_eq!(genesis.hash(), luca_2, "Luca of genesis and 1b is genesis"); // Test 1a to 1b let (backwards_3, luca_3, forwards_3) = archival_state - .find_path(mock_block_1_a.hash, mock_block_1_b.hash) + .find_path(mock_block_1_a.hash(), mock_block_1_b.hash()) .await; assert_eq!( - vec![mock_block_1_a.hash], + vec![mock_block_1_a.hash()], backwards_3, "Backwards path from 1a to 1b is 1a" ); assert_eq!( - vec![mock_block_1_b.hash], + vec![mock_block_1_b.hash()], forwards_3, "Forwards from 1a to block 1b is block 1b" ); - assert_eq!(genesis.hash, luca_3, "Luca of 1a and 1b is genesis"); + assert_eq!(genesis.hash(), luca_3, "Luca of 1a and 1b is genesis"); Ok(()) } @@ -1918,7 +1965,7 @@ mod archival_state_tests { let genesis = *archival_state.genesis_block.clone(); assert!( archival_state - .block_belongs_to_canonical_chain(&genesis.header, &genesis.header) + .block_belongs_to_canonical_chain(&genesis.kernel.header, &genesis.kernel.header) .await, "Genesis block is always part of the canonical chain, tip" ); @@ -1931,13 +1978,19 @@ mod archival_state_tests { add_block_to_archival_state(&mut archival_state, mock_block_1.clone()).await?; assert!( archival_state - .block_belongs_to_canonical_chain(&genesis.header, &mock_block_1.header) + .block_belongs_to_canonical_chain( + &genesis.kernel.header, + &mock_block_1.kernel.header + ) .await, "Genesis block is always part of the canonical chain, tip parent" ); assert!( archival_state - .block_belongs_to_canonical_chain(&mock_block_1.header, &mock_block_1.header) + .block_belongs_to_canonical_chain( + &mock_block_1.kernel.header, + &mock_block_1.kernel.header + ) .await, "Tip block is always part of the canonical chain" ); @@ -1964,18 +2017,24 @@ mod archival_state_tests { { assert!( archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_4_a.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_4_a.kernel.header + ) .await, "only chain {} is canonical", i ); - dag_walker_leash_prop(block.hash, mock_block_4_a.hash, &archival_state).await; - dag_walker_leash_prop(mock_block_4_a.hash, block.hash, &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_4_a.hash(), &archival_state).await; + dag_walker_leash_prop(mock_block_4_a.hash(), block.hash(), &archival_state).await; } assert!( archival_state - .block_belongs_to_canonical_chain(&genesis.header, &mock_block_4_a.header) + .block_belongs_to_canonical_chain( + &genesis.kernel.header, + &mock_block_4_a.kernel.header + ) .await, "Genesis block is always part of the canonical chain, block height is four" ); @@ -2006,13 +2065,16 @@ mod archival_state_tests { { assert!( archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_4_a.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_4_a.kernel.header + ) .await, "canonical chain {} is canonical", i ); - dag_walker_leash_prop(block.hash, mock_block_4_a.hash, &archival_state).await; - dag_walker_leash_prop(mock_block_4_a.hash, block.hash, &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_4_a.hash(), &archival_state).await; + dag_walker_leash_prop(mock_block_4_a.hash(), block.hash(), &archival_state).await; } // These blocks do not belong to the canonical chain since block 4_a has a higher PoW family @@ -2028,13 +2090,16 @@ mod archival_state_tests { { assert!( !archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_4_a.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_4_a.kernel.header + ) .await, "Stale chain {} is not canonical", i ); - dag_walker_leash_prop(block.hash, mock_block_4_a.hash, &archival_state).await; - dag_walker_leash_prop(mock_block_4_a.hash, block.hash, &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_4_a.hash(), &archival_state).await; + dag_walker_leash_prop(mock_block_4_a.hash(), block.hash(), &archival_state).await; } // Make a complicated tree and verify that the function identifies the correct blocks as part @@ -2110,13 +2175,16 @@ mod archival_state_tests { { assert!( archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_6_d.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_6_d.kernel.header + ) .await, "canonical chain {} is canonical, complicated", i ); - dag_walker_leash_prop(mock_block_6_d.hash, block.hash, &archival_state).await; - dag_walker_leash_prop(block.hash, mock_block_6_d.hash, &archival_state).await; + dag_walker_leash_prop(mock_block_6_d.hash(), block.hash(), &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_6_d.hash(), &archival_state).await; } for (i, block) in [ @@ -2141,13 +2209,16 @@ mod archival_state_tests { { assert!( !archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_6_d.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_6_d.kernel.header + ) .await, "Stale chain {} is not canonical", i ); - dag_walker_leash_prop(mock_block_6_d.hash, block.hash, &archival_state).await; - dag_walker_leash_prop(block.hash, mock_block_6_d.hash, &archival_state).await; + dag_walker_leash_prop(mock_block_6_d.hash(), block.hash(), &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_6_d.hash(), &archival_state).await; } // Make a new block, 6b, canonical and verify that all checks work @@ -2177,13 +2248,16 @@ mod archival_state_tests { { assert!( !archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_6_b.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_6_b.kernel.header + ) .await, "Stale chain {} is not canonical", i ); - dag_walker_leash_prop(mock_block_6_b.hash, block.hash, &archival_state).await; - dag_walker_leash_prop(block.hash, mock_block_6_b.hash, &archival_state).await; + dag_walker_leash_prop(mock_block_6_b.hash(), block.hash(), &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_6_b.hash(), &archival_state).await; } for (i, block) in [ @@ -2200,13 +2274,16 @@ mod archival_state_tests { { assert!( archival_state - .block_belongs_to_canonical_chain(&block.header, &mock_block_6_b.header) + .block_belongs_to_canonical_chain( + &block.kernel.header, + &mock_block_6_b.kernel.header + ) .await, "canonical chain {} is canonical, complicated", i ); - dag_walker_leash_prop(mock_block_6_b.hash, block.hash, &archival_state).await; - dag_walker_leash_prop(block.hash, mock_block_6_b.hash, &archival_state).await; + dag_walker_leash_prop(mock_block_6_b.hash(), block.hash(), &archival_state).await; + dag_walker_leash_prop(block.hash(), mock_block_6_b.hash(), &archival_state).await; } // An explicit test of `find_path` @@ -2223,30 +2300,30 @@ mod archival_state_tests { // // Note that in the later test, 6b becomes the tip. let (backwards, luca, forwards) = archival_state - .find_path(mock_block_5_e.hash, mock_block_6_b.hash) + .find_path(mock_block_5_e.hash(), mock_block_6_b.hash()) .await; assert_eq!( vec![ - mock_block_2_b.hash, - mock_block_3_b.hash, - mock_block_4_b.hash, - mock_block_5_b.hash, - mock_block_6_b.hash, + mock_block_2_b.hash(), + mock_block_3_b.hash(), + mock_block_4_b.hash(), + mock_block_5_b.hash(), + mock_block_6_b.hash(), ], forwards, "find_path forwards return value must match expected value" ); assert_eq!( vec![ - mock_block_5_e.hash, - mock_block_4_e.hash, - mock_block_3_d.hash, - mock_block_2_a.hash + mock_block_5_e.hash(), + mock_block_4_e.hash(), + mock_block_3_d.hash(), + mock_block_2_a.hash() ], backwards, "find_path backwards return value must match expected value" ); - assert_eq!(mock_block_1.hash, luca, "Luca must be block 1"); + assert_eq!(mock_block_1.hash(), luca, "Luca must be block 1"); Ok(()) } @@ -2259,7 +2336,7 @@ mod archival_state_tests { let genesis = archival_state.genesis_block.clone(); archival_state - .get_ancestor_block_digests(genesis.header.prev_block_digest, 10) + .get_ancestor_block_digests(genesis.kernel.header.prev_block_digest, 10) .await; } @@ -2272,15 +2349,15 @@ mod archival_state_tests { let own_receiving_address = own_wallet.nth_generation_spending_key(0).to_address(); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 10) + .get_ancestor_block_digests(genesis.hash(), 10) .await .is_empty()); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 1) + .get_ancestor_block_digests(genesis.hash(), 1) .await .is_empty()); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 0) + .get_ancestor_block_digests(genesis.hash(), 0) .await .is_empty()); @@ -2307,57 +2384,57 @@ mod archival_state_tests { .unwrap(); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 10) + .get_ancestor_block_digests(genesis.hash(), 10) .await .is_empty()); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 1) + .get_ancestor_block_digests(genesis.hash(), 1) .await .is_empty()); assert!(archival_state - .get_ancestor_block_digests(genesis.hash, 0) + .get_ancestor_block_digests(genesis.hash(), 0) .await .is_empty()); // Check that ancestors of block 1 and 2 return the right values let ancestors_of_1 = archival_state - .get_ancestor_block_digests(mock_block_1.hash, 10) + .get_ancestor_block_digests(mock_block_1.hash(), 10) .await; assert_eq!(1, ancestors_of_1.len()); - assert_eq!(genesis.hash, ancestors_of_1[0]); + assert_eq!(genesis.hash(), ancestors_of_1[0]); assert!(archival_state - .get_ancestor_block_digests(mock_block_1.hash, 0) + .get_ancestor_block_digests(mock_block_1.hash(), 0) .await .is_empty()); let ancestors_of_2 = archival_state - .get_ancestor_block_digests(mock_block_2.hash, 10) + .get_ancestor_block_digests(mock_block_2.hash(), 10) .await; assert_eq!(2, ancestors_of_2.len()); - assert_eq!(mock_block_1.hash, ancestors_of_2[0]); - assert_eq!(genesis.hash, ancestors_of_2[1]); + assert_eq!(mock_block_1.hash(), ancestors_of_2[0]); + assert_eq!(genesis.hash(), ancestors_of_2[1]); assert!(archival_state - .get_ancestor_block_digests(mock_block_2.hash, 0) + .get_ancestor_block_digests(mock_block_2.hash(), 0) .await .is_empty()); // Verify that max length is respected let ancestors_of_4_long = archival_state - .get_ancestor_block_digests(mock_block_4.hash, 10) + .get_ancestor_block_digests(mock_block_4.hash(), 10) .await; assert_eq!(4, ancestors_of_4_long.len()); - assert_eq!(mock_block_3.hash, ancestors_of_4_long[0]); - assert_eq!(mock_block_2.hash, ancestors_of_4_long[1]); - assert_eq!(mock_block_1.hash, ancestors_of_4_long[2]); - assert_eq!(genesis.hash, ancestors_of_4_long[3]); + assert_eq!(mock_block_3.hash(), ancestors_of_4_long[0]); + assert_eq!(mock_block_2.hash(), ancestors_of_4_long[1]); + assert_eq!(mock_block_1.hash(), ancestors_of_4_long[2]); + assert_eq!(genesis.hash(), ancestors_of_4_long[3]); let ancestors_of_4_short = archival_state - .get_ancestor_block_digests(mock_block_4.hash, 2) + .get_ancestor_block_digests(mock_block_4.hash(), 2) .await; assert_eq!(2, ancestors_of_4_short.len()); - assert_eq!(mock_block_3.hash, ancestors_of_4_short[0]); - assert_eq!(mock_block_2.hash, ancestors_of_4_short[1]); + assert_eq!(mock_block_3.hash(), ancestors_of_4_short[0]); + assert_eq!(mock_block_2.hash(), ancestors_of_4_short[1]); assert!(archival_state - .get_ancestor_block_digests(mock_block_4.hash, 0) + .get_ancestor_block_digests(mock_block_4.hash(), 0) .await .is_empty()); } @@ -2373,7 +2450,10 @@ mod archival_state_tests { let (mock_block_1, _, _) = make_mock_block_with_valid_pow(&genesis.clone(), None, own_receiving_address); archival_state - .write_block(&mock_block_1, Some(genesis.header.proof_of_work_family)) + .write_block( + &mock_block_1, + Some(genesis.kernel.header.proof_of_work_family), + ) .await?; // Verify that `LastFile` value is stored correctly @@ -2397,7 +2477,7 @@ mod archival_state_tests { .as_height_record(); assert_eq!(1, blocks_with_height_1.len()); - assert_eq!(mock_block_1.hash, blocks_with_height_1[0]); + assert_eq!(mock_block_1.hash(), blocks_with_height_1[0]); } // Verify that `File` value is stored correctly @@ -2414,11 +2494,11 @@ mod archival_state_tests { let expected_block_len_1 = bincode::serialize(&mock_block_1).unwrap().len(); assert_eq!(expected_block_len_1, last_file_record_1.file_size as usize); assert_eq!( - mock_block_1.header.height, + mock_block_1.kernel.header.height, last_file_record_1.min_block_height ); assert_eq!( - mock_block_1.header.height, + mock_block_1.kernel.header.height, last_file_record_1.max_block_height ); @@ -2430,17 +2510,17 @@ mod archival_state_tests { .unwrap() .as_tip_digest(); - assert_eq!(mock_block_1.hash, tip_digest); + assert_eq!(mock_block_1.hash(), tip_digest); // Verify that `Block` is stored correctly let actual_block: BlockRecord = archival_state .block_index_db - .get(BlockIndexKey::Block(mock_block_1.hash)) + .get(BlockIndexKey::Block(mock_block_1.hash())) .await .unwrap() .as_block_record(); - assert_eq!(mock_block_1.header, actual_block.block_header); + assert_eq!(mock_block_1.kernel.header, actual_block.block_header); assert_eq!( expected_block_len_1, actual_block.file_location.block_length @@ -2460,7 +2540,7 @@ mod archival_state_tests { archival_state .write_block( &mock_block_2, - Some(mock_block_1.header.proof_of_work_family), + Some(mock_block_1.kernel.header.proof_of_work_family), ) .await?; @@ -2482,7 +2562,7 @@ mod archival_state_tests { .unwrap() .as_height_record(); assert_eq!(1, blocks_with_height_1.len()); - assert_eq!(mock_block_1.hash, blocks_with_height_1[0]); + assert_eq!(mock_block_1.hash(), blocks_with_height_1[0]); } { @@ -2493,7 +2573,7 @@ mod archival_state_tests { .unwrap() .as_height_record(); assert_eq!(1, blocks_with_height_2.len()); - assert_eq!(mock_block_2.hash, blocks_with_height_2[0]); + assert_eq!(mock_block_2.hash(), blocks_with_height_2[0]); } // Verify that `File` value is updated correctly let expected_file_2: u32 = read_last_file.last_file; @@ -2510,11 +2590,11 @@ mod archival_state_tests { last_file_record_2.file_size as usize ); assert_eq!( - mock_block_1.header.height, + mock_block_1.kernel.header.height, last_file_record_2.min_block_height ); assert_eq!( - mock_block_2.header.height, + mock_block_2.kernel.header.height, last_file_record_2.max_block_height ); @@ -2525,17 +2605,20 @@ mod archival_state_tests { .await .unwrap() .as_tip_digest(); - assert_eq!(mock_block_2.hash, tip_digest_2); + assert_eq!(mock_block_2.hash(), tip_digest_2); // Verify that `Block` is stored correctly let actual_block_record_2: BlockRecord = archival_state .block_index_db - .get(BlockIndexKey::Block(mock_block_2.hash)) + .get(BlockIndexKey::Block(mock_block_2.hash())) .await .unwrap() .as_block_record(); - assert_eq!(mock_block_2.header, actual_block_record_2.block_header); + assert_eq!( + mock_block_2.kernel.header, + actual_block_record_2.block_header + ); assert_eq!( expected_block_len_2, actual_block_record_2.file_location.block_length @@ -2558,48 +2641,50 @@ mod archival_state_tests { .get_block_from_block_record(actual_block_record_2) .unwrap(); assert_eq!(mock_block_2, block_from_block_record); - assert_eq!(mock_block_2.hash, block_from_block_record.hash); + assert_eq!(mock_block_2.hash(), block_from_block_record.hash()); // Test `get_block_header` let block_header_2 = archival_state - .get_block_header(mock_block_2.hash) + .get_block_header(mock_block_2.hash()) .await .unwrap(); - assert_eq!(mock_block_2.header, block_header_2); + assert_eq!(mock_block_2.kernel.header, block_header_2); // Test `get_block_header` { let block_header_2_from_lock_method = archival_state - .get_block_header(mock_block_2.hash) + .get_block_header(mock_block_2.hash()) .await .unwrap(); - assert_eq!(mock_block_2.header, block_header_2_from_lock_method); + assert_eq!(mock_block_2.kernel.header, block_header_2_from_lock_method); - let genesis_header_from_lock_method = - archival_state.get_block_header(genesis.hash).await.unwrap(); - assert_eq!(genesis.header, genesis_header_from_lock_method); + let genesis_header_from_lock_method = archival_state + .get_block_header(genesis.hash()) + .await + .unwrap(); + assert_eq!(genesis.kernel.header, genesis_header_from_lock_method); } // Test `block_height_to_block_headers` let block_headers_of_height_2 = archival_state.block_height_to_block_headers(2.into()).await; assert_eq!(1, block_headers_of_height_2.len()); - assert_eq!(mock_block_2.header, block_headers_of_height_2[0]); + assert_eq!(mock_block_2.kernel.header, block_headers_of_height_2[0]); // Test `get_children_blocks` let children_of_mock_block_1 = archival_state - .get_children_blocks(&mock_block_1.header) + .get_children_blocks(&mock_block_1.kernel.header) .await; assert_eq!(1, children_of_mock_block_1.len()); - assert_eq!(mock_block_2.header, children_of_mock_block_1[0]); + assert_eq!(mock_block_2.kernel.header, children_of_mock_block_1[0]); // Test `get_ancestor_block_digests` let ancestor_digests = archival_state - .get_ancestor_block_digests(mock_block_2.hash, 10) + .get_ancestor_block_digests(mock_block_2.hash(), 10) .await; assert_eq!(2, ancestor_digests.len()); - assert_eq!(Hash::hash(&mock_block_1.header), ancestor_digests[0]); - assert_eq!(Hash::hash(&genesis.header), ancestor_digests[1]); + assert_eq!(Hash::hash(&mock_block_1.kernel.header), ancestor_digests[0]); + assert_eq!(Hash::hash(&genesis.kernel.header), ancestor_digests[1]); Ok(()) } diff --git a/src/models/state/mempool.rs b/src/models/state/mempool.rs index d325879a6..4a0a27999 100644 --- a/src/models/state/mempool.rs +++ b/src/models/state/mempool.rs @@ -294,6 +294,7 @@ impl Mempool { // Removes the transaction from the mempool if they are not as this would // mean that at least on of the mempool transaction's inputs are spent in this block. let sbf_indices_set_by_block: HashSet<_> = block + .kernel .body .transaction .kernel @@ -638,6 +639,7 @@ mod tests { debug!( "Just made block with previous mutator set hash {}", block_3_with_updated_tx + .kernel .body .previous_mutator_set_accumulator .hash() @@ -646,6 +648,7 @@ mod tests { debug!( "Just made block with next mutator set hash {}", block_3_with_updated_tx + .kernel .body .next_mutator_set_accumulator .hash() @@ -673,7 +676,7 @@ mod tests { } let (mut block_14, _, _) = make_mock_block(&previous_block, None, other_receiver_address); - assert_eq!(Into::::into(14), block_14.header.height); + assert_eq!(Into::::into(14), block_14.kernel.header.height); tx_by_other_updated = mempool.get_transactions_for_block(usize::MAX)[0].clone(); block_14.accumulate_transaction(tx_by_other_updated); assert!( diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index a6e4099df..216b7780d 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -393,11 +393,11 @@ impl GlobalState { // todo: accomodate a future change whereby this function also returns the matching spending keys let spendable_utxos_and_mps: Vec<(Utxo, LockScript, MsMembershipProof)> = self .wallet_state - .allocate_sufficient_input_funds_from_lock(total_spend, bc_tip.hash) + .allocate_sufficient_input_funds_from_lock(total_spend, bc_tip.hash()) .await?; // Create all removal records. These must be relative to the block tip. - let msa_tip = &bc_tip.body.next_mutator_set_accumulator; + let msa_tip = &bc_tip.kernel.body.next_mutator_set_accumulator; let mut inputs: Vec> = vec![]; let mut input_amount: Amount = Amount::zero(); for (spendable_utxo, _lock_script, mp) in spendable_utxos_and_mps.iter() { @@ -444,7 +444,7 @@ impl GlobalState { let change_sender_randomness = self .wallet_state .wallet_secret - .generate_sender_randomness(bc_tip.header.height, receiver_digest); + .generate_sender_randomness(bc_tip.kernel.header.height, receiver_digest); let change_addition_record = commit::( Hash::hash(&change_utxo), change_sender_randomness, @@ -774,10 +774,10 @@ impl GlobalState { .await? .unwrap(); - debug!("MUTXO confirmed at height {confirming_block_height}, reverting for height {} on abandoned chain", revert_block.header.height); + debug!("MUTXO confirmed at height {confirming_block_height}, reverting for height {} on abandoned chain", revert_block.kernel.header.height); // revert removals - let removal_records = revert_block.body.transaction.kernel.inputs.clone(); + let removal_records = revert_block.kernel.body.transaction.kernel.inputs.clone(); for removal_record in removal_records.iter().rev() { // membership_proof.revert_update_from_removal(&removal); membership_proof @@ -786,8 +786,11 @@ impl GlobalState { } // revert additions - let previous_mutator_set = - revert_block.body.previous_mutator_set_accumulator.clone(); + let previous_mutator_set = revert_block + .kernel + .body + .previous_mutator_set_accumulator + .clone(); membership_proof.revert_update_from_batch_addition(&previous_mutator_set); // unset spent_in_block field if the UTXO was spent in this block @@ -818,9 +821,13 @@ impl GlobalState { .get_block(apply_block_hash) .await? .unwrap(); - let addition_records = apply_block.body.transaction.kernel.outputs; - let removal_records = apply_block.body.transaction.kernel.inputs; - let mut block_msa = apply_block.body.previous_mutator_set_accumulator.clone(); + let addition_records = apply_block.kernel.body.transaction.kernel.outputs; + let removal_records = apply_block.kernel.body.transaction.kernel.inputs; + let mut block_msa = apply_block + .kernel + .body + .previous_mutator_set_accumulator + .clone(); // apply additions for addition_record in addition_records.iter() { @@ -842,7 +849,10 @@ impl GlobalState { block_msa.remove(removal_record); } - assert_eq!(block_msa, apply_block.body.next_mutator_set_accumulator); + assert_eq!( + block_msa, + apply_block.kernel.body.next_mutator_set_accumulator + ); } // store updated membership proof @@ -945,7 +955,7 @@ impl GlobalState { self.chain.archival_state_mut().block_index_db.flush().await; // persist archival_mutator_set, with sync label - let hash = self.chain.archival_state().get_latest_block().await.hash; + let hash = self.chain.archival_state().get_latest_block().await.hash(); self.chain .archival_state_mut() .archival_mutator_set @@ -985,7 +995,7 @@ impl GlobalState { coinbase_utxo_info: Option, ) -> Result<()> { // get proof_of_work_family for tip - let tip_proof_of_work_family = self.chain.light_state().header.proof_of_work_family; + let tip_proof_of_work_family = self.chain.light_state().kernel.header.proof_of_work_family; // Apply the updates self.chain @@ -1078,11 +1088,12 @@ mod global_state_tests { ) -> bool { let monitored_utxos = wallet_state.wallet_db.monitored_utxos(); for (_idx, monitored_utxo) in monitored_utxos.iter() { - let current_mp = monitored_utxo.get_membership_proof_for_block(tip_block.hash); + let current_mp = monitored_utxo.get_membership_proof_for_block(tip_block.hash()); match current_mp { Some(mp) => { if !tip_block + .kernel .body .next_mutator_set_accumulator .verify(Hash::hash(&monitored_utxo.utxo), &mp) @@ -1250,7 +1261,7 @@ mod global_state_tests { .1, ); assert_eq!( - mock_block_1.hash, + mock_block_1.hash(), own_premine_mutxo .get_latest_membership_proof_entry() .unwrap() @@ -1281,7 +1292,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &mock_block_1a, - Some(mock_block_1a.header.proof_of_work_family), + Some(mock_block_1a.kernel.header.proof_of_work_family), ) .await?; } @@ -1294,19 +1305,19 @@ mod global_state_tests { assert!( global_state .wallet_state - .is_synced_to(genesis_block.hash) + .is_synced_to(genesis_block.hash()) .await ); assert!( !global_state .wallet_state - .is_synced_to(mock_block_1a.hash) + .is_synced_to(mock_block_1a.hash()) .await ); // Call resync global_state - .resync_membership_proofs_from_stored_blocks(mock_block_1a.hash) + .resync_membership_proofs_from_stored_blocks(mock_block_1a.hash()) .await .unwrap(); @@ -1314,7 +1325,7 @@ mod global_state_tests { assert!( global_state .wallet_state - .is_synced_to(mock_block_1a.hash) + .is_synced_to(mock_block_1a.hash()) .await ); @@ -1348,7 +1359,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &mock_block_1a, - Some(mock_block_1a.header.proof_of_work_family), + Some(mock_block_1a.kernel.header.proof_of_work_family), ) .await?; global_state @@ -1371,7 +1382,7 @@ mod global_state_tests { // Verify that wallet has monitored UTXOs, from genesis and from block_1a let wallet_status = global_state .wallet_state - .get_wallet_status_from_lock(mock_block_1a.hash); + .get_wallet_status_from_lock(mock_block_1a.hash()); assert_eq!(2, wallet_status.synced_unspent.len()); // Make a new fork from genesis that makes us lose the coinbase UTXO of block 1a @@ -1385,7 +1396,10 @@ mod global_state_tests { global_state .chain .archival_state_mut() - .write_block(&next_block, Some(next_block.header.proof_of_work_family)) + .write_block( + &next_block, + Some(next_block.kernel.header.proof_of_work_family), + ) .await?; global_state .wallet_state @@ -1397,14 +1411,14 @@ mod global_state_tests { // Call resync which fails to sync the UTXO that was abandoned when block 1a was abandoned global_state - .resync_membership_proofs_from_stored_blocks(parent_block.hash) + .resync_membership_proofs_from_stored_blocks(parent_block.hash()) .await .unwrap(); // Verify that one MUTXO is unsynced, and that 1 (from genesis) is synced let wallet_status_after_forking = global_state .wallet_state - .get_wallet_status_from_lock(parent_block.hash); + .get_wallet_status_from_lock(parent_block.hash()); assert_eq!(1, wallet_status_after_forking.synced_unspent.len()); assert_eq!(1, wallet_status_after_forking.unsynced_unspent.len()); @@ -1414,13 +1428,19 @@ mod global_state_tests { assert!( !monitored_utxos .get(0) - .was_abandoned(&parent_block.header, global_state.chain.archival_state()) + .was_abandoned( + &parent_block.kernel.header, + global_state.chain.archival_state() + ) .await ); assert!( monitored_utxos .get(1) - .was_abandoned(&parent_block.header, global_state.chain.archival_state()) + .was_abandoned( + &parent_block.kernel.header, + global_state.chain.archival_state() + ) .await ); @@ -1443,7 +1463,7 @@ mod global_state_tests { // 1. Create new block 1a where we receive a coinbase UTXO, store it let genesis_block = global_state.chain.archival_state().get_latest_block().await; - assert!(genesis_block.header.height.is_genesis()); + assert!(genesis_block.kernel.header.height.is_genesis()); let (mock_block_1a, coinbase_utxo_1a, cb_utxo_output_randomness_1a) = make_mock_block(&genesis_block, None, own_receiving_address); { @@ -1452,7 +1472,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &mock_block_1a, - Some(mock_block_1a.header.proof_of_work_family), + Some(mock_block_1a.kernel.header.proof_of_work_family), ) .await?; global_state @@ -1474,7 +1494,7 @@ mod global_state_tests { // Verify that UTXO was recorded let wallet_status_after_1a = global_state .wallet_state - .get_wallet_status_from_lock(mock_block_1a.hash); + .get_wallet_status_from_lock(mock_block_1a.hash()); assert_eq!(2, wallet_status_after_1a.synced_unspent.len()); } @@ -1488,7 +1508,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &next_a_block, - Some(next_a_block.header.proof_of_work_family), + Some(next_a_block.kernel.header.proof_of_work_family), ) .await?; global_state @@ -1502,7 +1522,7 @@ mod global_state_tests { // Verify that all both MUTXOs have synced MPs let wallet_status_on_a_fork = global_state .wallet_state - .get_wallet_status_from_lock(fork_a_block.hash); + .get_wallet_status_from_lock(fork_a_block.hash()); assert_eq!(2, wallet_status_on_a_fork.synced_unspent.len()); @@ -1516,7 +1536,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &next_b_block, - Some(next_b_block.header.proof_of_work_family), + Some(next_b_block.kernel.header.proof_of_work_family), ) .await?; global_state @@ -1530,7 +1550,7 @@ mod global_state_tests { // Verify that there are zero MUTXOs with synced MPs let wallet_status_on_b_fork_before_resync = global_state .wallet_state - .get_wallet_status_from_lock(fork_b_block.hash); + .get_wallet_status_from_lock(fork_b_block.hash()); assert_eq!( 0, wallet_status_on_b_fork_before_resync.synced_unspent.len() @@ -1542,12 +1562,12 @@ mod global_state_tests { // Run the resync and verify that MPs are synced global_state - .resync_membership_proofs_from_stored_blocks(fork_b_block.hash) + .resync_membership_proofs_from_stored_blocks(fork_b_block.hash()) .await .unwrap(); let wallet_status_on_b_fork_after_resync = global_state .wallet_state - .get_wallet_status_from_lock(fork_b_block.hash); + .get_wallet_status_from_lock(fork_b_block.hash()); assert_eq!(2, wallet_status_on_b_fork_after_resync.synced_unspent.len()); assert_eq!( 0, @@ -1566,7 +1586,7 @@ mod global_state_tests { .archival_state_mut() .write_block( &next_c_block, - Some(next_c_block.header.proof_of_work_family), + Some(next_c_block.kernel.header.proof_of_work_family), ) .await?; global_state @@ -1580,7 +1600,7 @@ mod global_state_tests { // Verify that there are zero MUTXOs with synced MPs let wallet_status_on_c_fork_before_resync = global_state .wallet_state - .get_wallet_status_from_lock(fork_c_block.hash); + .get_wallet_status_from_lock(fork_c_block.hash()); assert_eq!( 0, wallet_status_on_c_fork_before_resync.synced_unspent.len() @@ -1593,12 +1613,12 @@ mod global_state_tests { // Run the resync and verify that UTXO from genesis is synced, but that // UTXO from 1a is not synced. global_state - .resync_membership_proofs_from_stored_blocks(fork_c_block.hash) + .resync_membership_proofs_from_stored_blocks(fork_c_block.hash()) .await .unwrap(); let wallet_status_on_c_fork_after_resync = global_state .wallet_state - .get_wallet_status_from_lock(fork_c_block.hash); + .get_wallet_status_from_lock(fork_c_block.hash()); assert_eq!(1, wallet_status_on_c_fork_after_resync.synced_unspent.len()); assert_eq!( 1, @@ -1610,13 +1630,19 @@ mod global_state_tests { assert!( !monitored_utxos .get(0) - .was_abandoned(&fork_c_block.header, global_state.chain.archival_state()) + .was_abandoned( + &fork_c_block.kernel.header, + global_state.chain.archival_state() + ) .await ); assert!( monitored_utxos .get(1) - .was_abandoned(&fork_c_block.header, global_state.chain.archival_state()) + .was_abandoned( + &fork_c_block.kernel.header, + global_state.chain.archival_state() + ) .await ); diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index e6276b34a..cd634f4bf 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -428,10 +428,11 @@ mod wallet_tests { let genesis_block_output_utxo = monitored_utxos[0].utxo.clone(); let ms_membership_proof = monitored_utxos[0] - .get_membership_proof_for_block(next_block.hash) + .get_membership_proof_for_block(next_block.hash()) .unwrap(); assert!( next_block + .kernel .body .next_mutator_set_accumulator .verify(Hash::hash(&genesis_block_output_utxo), &ms_membership_proof), @@ -488,7 +489,7 @@ mod wallet_tests { let expected_utxos = own_wallet_state.expected_utxos.get_all_expected_utxos(); assert_eq!(1, expected_utxos.len(), "B: Expected UTXO list must have length 1 after block registration, due to potential reorganizations"); assert_eq!( - block_1.hash, + block_1.hash(), expected_utxos[0].mined_in_block.unwrap().0, "Expected UTXO must be registered as being mined" ); @@ -503,9 +504,10 @@ mod wallet_tests { { let block_1_tx_output_digest = Hash::hash(&block_1_coinbase_utxo); let ms_membership_proof = monitored_utxos[0] - .get_membership_proof_for_block(block_1.hash) + .get_membership_proof_for_block(block_1.hash()) .unwrap(); let membership_proof_is_valid = block_1 + .kernel .body .next_mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); @@ -520,9 +522,10 @@ mod wallet_tests { { let block_1_tx_output_digest = Hash::hash(&block_1_coinbase_utxo); let ms_membership_proof = monitored_utxos[0] - .get_membership_proof_for_block(block_1.hash) + .get_membership_proof_for_block(block_1.hash()) .unwrap(); let membership_proof_is_valid = block_3 + .kernel .body .next_mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); @@ -543,9 +546,10 @@ mod wallet_tests { { let block_1_tx_output_digest = Hash::hash(&block_1_coinbase_utxo); let ms_membership_proof = monitored_utxos[0] - .get_membership_proof_for_block(block_3.hash) + .get_membership_proof_for_block(block_3.hash()) .unwrap(); let membership_proof_is_valid = block_3 + .kernel .body .next_mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); @@ -590,7 +594,7 @@ mod wallet_tests { assert_eq!( 1, own_wallet_state - .allocate_sufficient_input_funds(Amount::one(), block_1.hash) + .allocate_sufficient_input_funds(Amount::one(), block_1.hash()) .await .unwrap() .len() @@ -600,7 +604,7 @@ mod wallet_tests { own_wallet_state .allocate_sufficient_input_funds( mining_reward.checked_sub(&Amount::one()).unwrap(), - block_1.hash + block_1.hash() ) .await .unwrap() @@ -609,7 +613,7 @@ mod wallet_tests { assert_eq!( 1, own_wallet_state - .allocate_sufficient_input_funds(mining_reward, block_1.hash) + .allocate_sufficient_input_funds(mining_reward, block_1.hash()) .await .unwrap() .len() @@ -617,7 +621,7 @@ mod wallet_tests { // Cannot allocate more than we have: `mining_reward` assert!(own_wallet_state - .allocate_sufficient_input_funds(mining_reward + Amount::one(), block_1.hash) + .allocate_sufficient_input_funds(mining_reward + Amount::one(), block_1.hash()) .await .is_err()); @@ -645,7 +649,7 @@ mod wallet_tests { assert_eq!( 5, own_wallet_state - .allocate_sufficient_input_funds(mining_reward.scalar_mul(5), next_block.hash) + .allocate_sufficient_input_funds(mining_reward.scalar_mul(5), next_block.hash()) .await .unwrap() .len() @@ -655,7 +659,7 @@ mod wallet_tests { own_wallet_state .allocate_sufficient_input_funds( mining_reward.scalar_mul(5) + Amount::one(), - next_block.hash + next_block.hash() ) .await .unwrap() @@ -666,7 +670,7 @@ mod wallet_tests { assert_eq!( 22, own_wallet_state - .allocate_sufficient_input_funds(expected_balance, next_block.hash) + .allocate_sufficient_input_funds(expected_balance, next_block.hash()) .await .unwrap() .len() @@ -674,14 +678,14 @@ mod wallet_tests { // Cannot allocate more than we have: 22 * mining reward assert!(own_wallet_state - .allocate_sufficient_input_funds(expected_balance + Amount::one(), next_block.hash) + .allocate_sufficient_input_funds(expected_balance + Amount::one(), next_block.hash()) .await .is_err()); // Make a block that spends an input, then verify that this is reflected by // the allocator. let two_utxos = own_wallet_state - .allocate_sufficient_input_funds(mining_reward.scalar_mul(2), next_block.hash) + .allocate_sufficient_input_funds(mining_reward.scalar_mul(2), next_block.hash()) .await .unwrap(); assert_eq!( @@ -695,11 +699,21 @@ mod wallet_tests { let other_wallet = WalletSecret::new_random(); let other_wallet_recipient_address = other_wallet.nth_generation_spending_key(0).to_address(); - assert_eq!(Into::::into(22u64), next_block.header.height); + assert_eq!( + Into::::into(22u64), + next_block.kernel.header.height + ); (next_block, _, _) = make_mock_block(&next_block.clone(), None, own_spending_key.to_address()); - assert_eq!(Into::::into(23u64), next_block.header.height); - let msa_tip_previous = next_block.body.previous_mutator_set_accumulator.clone(); + assert_eq!( + Into::::into(23u64), + next_block.kernel.header.height + ); + let msa_tip_previous = next_block + .kernel + .body + .previous_mutator_set_accumulator + .clone(); let receiver_data = vec![UtxoReceiverData { utxo: Utxo { @@ -729,7 +743,7 @@ mod wallet_tests { assert_eq!( 20, own_wallet_state - .allocate_sufficient_input_funds(2000.into(), next_block.hash) + .allocate_sufficient_input_funds(2000.into(), next_block.hash()) .await .unwrap() .len() @@ -737,7 +751,7 @@ mod wallet_tests { // Cannot allocate more than we have: 2000 assert!(own_wallet_state - .allocate_sufficient_input_funds(2001.into(), next_block.hash) + .allocate_sufficient_input_funds(2001.into(), next_block.hash()) .await .is_err()); @@ -781,7 +795,7 @@ mod wallet_tests { .wallet_state .wallet_secret .generate_sender_randomness( - genesis_block.header.height, + genesis_block.kernel.header.height, own_address.privacy_digest, ), utxo: Utxo { @@ -796,7 +810,7 @@ mod wallet_tests { .wallet_state .wallet_secret .generate_sender_randomness( - genesis_block.header.height, + genesis_block.kernel.header.height, own_address.privacy_digest, ), utxo: Utxo { @@ -867,10 +881,10 @@ mod wallet_tests { // Verify that all monitored UTXOs have valid membership proofs for monitored_utxo in monitored_utxos { assert!( - block_1.body.next_mutator_set_accumulator.verify( + block_1.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_1.hash) + .get_membership_proof_for_block(block_1.hash()) .unwrap() ), "All membership proofs must be valid after block 1" @@ -914,10 +928,10 @@ mod wallet_tests { ); for monitored_utxo in monitored_utxos { assert!( - block_18.body.next_mutator_set_accumulator.verify( + block_18.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_18.hash) + .get_membership_proof_for_block(block_18.hash()) .unwrap() ), "All membership proofs must be valid after block 18" @@ -927,12 +941,12 @@ mod wallet_tests { // Sanity check assert_eq!( Into::::into(18u64), - block_18.header.height, + block_18.kernel.header.height, "Block height must be 18 after genesis and 18 blocks being mined" ); // Check that `WalletStatus` is returned correctly - let wallet_status = { own_wallet_state.get_wallet_status_from_lock(block_18.hash) }; + let wallet_status = { own_wallet_state.get_wallet_status_from_lock(block_18.hash()) }; assert_eq!( 19, wallet_status.synced_unspent.len(), @@ -972,7 +986,7 @@ mod wallet_tests { let monitored_utxos_at_2b: Vec<_> = get_monitored_utxos(&own_wallet_state) .await .into_iter() - .filter(|x| x.is_synced_to(block_2_b.hash)) + .filter(|x| x.is_synced_to(block_2_b.hash())) .collect(); assert_eq!( 2, @@ -983,10 +997,10 @@ mod wallet_tests { // Verify that all monitored UTXOs (with synced MPs) have valid membership proofs for monitored_utxo in monitored_utxos_at_2b.iter() { assert!( - block_2_b.body.next_mutator_set_accumulator.verify( + block_2_b.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_2_b.hash) + .get_membership_proof_for_block(block_2_b.hash()) .unwrap() ), "All synced membership proofs must be valid after block 2b fork" @@ -1003,7 +1017,7 @@ mod wallet_tests { let monitored_utxos_block_19: Vec<_> = get_monitored_utxos(&own_wallet_state) .await .into_iter() - .filter(|monitored_utxo| monitored_utxo.is_synced_to(block_19.hash)) + .filter(|monitored_utxo| monitored_utxo.is_synced_to(block_19.hash())) .collect(); assert_eq!( 2 + 17, @@ -1014,10 +1028,10 @@ mod wallet_tests { // Verify that all monitored UTXOs have valid membership proofs for monitored_utxo in monitored_utxos_block_19.iter() { assert!( - block_19.body.next_mutator_set_accumulator.verify( + block_19.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_19.hash) + .get_membership_proof_for_block(block_19.hash()) .unwrap() ), "All membership proofs must be valid after block 19" @@ -1076,7 +1090,7 @@ mod wallet_tests { let monitored_utxos_3b: Vec<_> = get_monitored_utxos(&own_wallet_state) .await .into_iter() - .filter(|x| x.is_synced_to(block_3_b.hash)) + .filter(|x| x.is_synced_to(block_3_b.hash())) .collect(); assert_eq!( 4, @@ -1096,10 +1110,10 @@ mod wallet_tests { for monitored_utxo in monitored_utxos_3b { assert!( monitored_utxo.spent_in_block.is_some() - || block_3_b.body.next_mutator_set_accumulator.verify( + || block_3_b.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_3_b.hash) + .get_membership_proof_for_block(block_3_b.hash()) .unwrap() ), "All membership proofs of unspent UTXOs must be valid after block 3b" @@ -1117,7 +1131,7 @@ mod wallet_tests { let monitored_utxos_20: Vec<_> = get_monitored_utxos(&own_wallet_state) .await .into_iter() - .filter(|x| x.is_synced_to(block_20.hash)) + .filter(|x| x.is_synced_to(block_20.hash())) .collect(); assert_eq!( 19, @@ -1127,10 +1141,10 @@ mod wallet_tests { for monitored_utxo in monitored_utxos_20.iter() { assert!( monitored_utxo.spent_in_block.is_some() - || block_20.body.next_mutator_set_accumulator.verify( + || block_20.kernel.body.next_mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo - .get_membership_proof_for_block(block_20.hash) + .get_membership_proof_for_block(block_20.hash()) .unwrap() ), "All membership proofs of unspent UTXOs must be valid after block 20" diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index 9a4486e54..b5de243dd 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -275,7 +275,7 @@ impl WalletState { /// Update wallet state with new block. Assumes the given block /// is valid and that the wallet state is not up to date yet. pub async fn update_wallet_state_with_new_block(&mut self, block: &Block) -> Result<()> { - let transaction: Transaction = block.body.transaction.clone(); + let transaction: Transaction = block.kernel.body.transaction.clone(); let spent_inputs: Vec<(Utxo, AbsoluteIndexSet, u64)> = self.scan_for_spent_utxos(&transaction); @@ -323,7 +323,9 @@ impl WalletState { for (i, monitored_utxo) in monitored_utxos.iter() { let utxo_digest = Hash::hash(&monitored_utxo.utxo); - match monitored_utxo.get_membership_proof_for_block(block.header.prev_block_digest) { + match monitored_utxo + .get_membership_proof_for_block(block.kernel.header.prev_block_digest) + { Some(ms_mp) => { debug!("Found valid mp for UTXO"); let insert_ret = valid_membership_proofs_and_own_utxo_count.insert( @@ -349,7 +351,7 @@ impl WalletState { None => String::from("No info about when UTXO was confirmed."), }; warn!( - "Unable to find valid membership proof for UTXO with digest {utxo_digest}. {confirmed_in_block_info} Current block height is {}", block.header.height + "Unable to find valid membership proof for UTXO with digest {utxo_digest}. {confirmed_in_block_info} Current block height is {}", block.kernel.header.height ); } } @@ -360,15 +362,26 @@ impl WalletState { // a) update all existing MS membership proofs // b) Register incoming transactions and derive their membership proofs let mut changed_mps = vec![]; - let mut msa_state: MutatorSetAccumulator = - block.body.previous_mutator_set_accumulator.to_owned(); + let mut msa_state: MutatorSetAccumulator = block + .kernel + .body + .previous_mutator_set_accumulator + .to_owned(); let mut removal_records = transaction.kernel.inputs.clone(); removal_records.reverse(); let mut removal_records: Vec<&mut RemovalRecord> = removal_records.iter_mut().collect::>(); - for addition_record in block.body.transaction.kernel.outputs.clone().into_iter() { + for addition_record in block + .kernel + .body + .transaction + .kernel + .outputs + .clone() + .into_iter() + { // Don't pull this declaration out of the for-loop since the hash map can grow // within this loop. let utxo_digests = valid_membership_proofs_and_own_utxo_count @@ -407,8 +420,8 @@ impl WalletState { let receiver_preimage = addition_record_to_utxo_info[&addition_record].2; info!( "Received UTXO in block {}, height {}: value = {}", - block.hash.emojihash(), - block.header.height, + block.hash().emojihash(), + block.kernel.header.height, utxo.coins .iter() .filter(|coin| coin.type_script_hash == NATIVE_COIN_TYPESCRIPT_DIGEST) @@ -443,9 +456,9 @@ impl WalletState { // Add the new UTXO to the list of monitored UTXOs let mut mutxo = MonitoredUtxo::new(utxo, self.number_of_mps_per_utxo); mutxo.confirmed_in_block = Some(( - block.hash, - Duration::from_millis(block.header.timestamp.value()), - block.header.height, + block.hash(), + Duration::from_millis(block.kernel.header.timestamp.value()), + block.kernel.header.height, )); monitored_utxos.push(mutxo); } @@ -458,7 +471,7 @@ impl WalletState { let mutxo_with_valid_mps = monitored_utxos .iter() .filter(|(_i, mutxo)| { - mutxo.is_synced_to(block.header.prev_block_digest) + mutxo.is_synced_to(block.kernel.header.prev_block_digest) || mutxo.blockhash_to_membership_proof.is_empty() }) .count(); @@ -472,7 +485,7 @@ impl WalletState { debug!("Block has {} removal records", removal_records.len()); debug!( "Transaction has {} inputs", - block.body.transaction.kernel.inputs.len() + block.kernel.body.transaction.kernel.inputs.len() ); let mut block_tx_input_count = 0; while let Some(removal_record) = removal_records.pop() { @@ -509,9 +522,9 @@ impl WalletState { let mut spent_mutxo = monitored_utxos.get(*mutxo_list_index); spent_mutxo.spent_in_block = Some(( - block.hash, - Duration::from_millis(block.header.timestamp.value()), - block.header.height, + block.hash(), + Duration::from_millis(block.kernel.header.timestamp.value()), + block.kernel.header.height, )); monitored_utxos.set(*mutxo_list_index, spent_mutxo); } @@ -523,7 +536,12 @@ impl WalletState { // Sanity check that `msa_state` agrees with the mutator set from the applied block assert_eq!( - block.body.next_mutator_set_accumulator.clone().hash(), + block + .kernel + .body + .next_mutator_set_accumulator + .clone() + .hash(), msa_state.hash(), "Mutator set in wallet-handler must agree with that from applied block" ); @@ -544,7 +562,7 @@ impl WalletState { { let StrongUtxoKey { utxo_digest, .. } = strong_utxo_key; let mut monitored_utxo = monitored_utxos.get(*own_utxo_index); - monitored_utxo.add_membership_proof_for_tip(block.hash, updated_ms_mp.to_owned()); + monitored_utxo.add_membership_proof_for_tip(block.hash(), updated_ms_mp.to_owned()); // Sanity check that membership proofs of non-spent transactions are still valid assert!( @@ -565,7 +583,7 @@ impl WalletState { self.store_utxo_ms_recovery_data(item)?; } - self.wallet_db.set_sync_label(block.hash); + self.wallet_db.set_sync_label(block.hash()); self.wallet_db.persist(); // Mark all expected UTXOs that were received in this block as received @@ -573,7 +591,7 @@ impl WalletState { .into_iter() .for_each(|(addition_rec, _, _, _)| { self.expected_utxos - .mark_as_received(addition_rec, block.hash) + .mark_as_received(addition_rec, block.hash()) .expect("Expected UTXO must be present when marking it as received") }); @@ -768,7 +786,10 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block(&new_block, Some(latest_block.header.proof_of_work_family)) + .write_block( + &new_block, + Some(latest_block.kernel.header.proof_of_work_family), + ) .await .unwrap(); own_global_state @@ -814,7 +835,10 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block(&block_3a, Some(latest_block.header.proof_of_work_family)) + .write_block( + &block_3a, + Some(latest_block.kernel.header.proof_of_work_family), + ) .await .unwrap(); own_global_state @@ -858,7 +882,10 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block(&block_3b, Some(latest_block.header.proof_of_work_family)) + .write_block( + &block_3b, + Some(latest_block.kernel.header.proof_of_work_family), + ) .await .unwrap(); own_global_state @@ -900,7 +927,10 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block(&new_block, Some(latest_block.header.proof_of_work_family)) + .write_block( + &new_block, + Some(latest_block.kernel.header.proof_of_work_family), + ) .await .unwrap(); own_global_state @@ -941,7 +971,10 @@ mod tests { own_global_state .chain .archival_state_mut() - .write_block(&block_12, Some(latest_block.header.proof_of_work_family)) + .write_block( + &block_12, + Some(latest_block.kernel.header.proof_of_work_family), + ) .await .unwrap(); own_global_state @@ -966,8 +999,8 @@ mod tests { assert!(prune_count_12.is_one()); assert_eq!( ( - block_12.hash, - Duration::from_millis(block_12.header.timestamp.value()), + block_12.hash(), + Duration::from_millis(block_12.kernel.header.timestamp.value()), 12u64.into() ), own_global_state diff --git a/src/peer_loop.rs b/src/peer_loop.rs index b34dfda4f..1f87ef816 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -120,36 +120,36 @@ impl PeerLoopHandler { if !new_block.has_proof_of_work(previous_block) { warn!( "Received invalid proof-of-work for block of height {} from peer with IP {}", - new_block.header.height, self.peer_address + new_block.kernel.header.height, self.peer_address ); - warn!("Difficulty is {}.", previous_block.header.difficulty); + warn!("Difficulty is {}.", previous_block.kernel.header.difficulty); warn!( "Proof of work should be {} (or more) but was [{}].", - Block::difficulty_to_digest_threshold(previous_block.header.difficulty), - new_block.hash.values().iter().join(", ") + Block::difficulty_to_digest_threshold(previous_block.kernel.header.difficulty), + new_block.hash().values().iter().join(", ") ); self.punish(PeerSanctionReason::InvalidBlock(( - new_block.header.height, - new_block.hash, + new_block.kernel.header.height, + new_block.hash(), ))) .await?; bail!("Failed to validate block due to insufficient PoW"); } else if !new_block.is_valid(previous_block) { warn!( "Received invalid block of height {} from peer with IP {}", - new_block.header.height, self.peer_address + new_block.kernel.header.height, self.peer_address ); self.punish(PeerSanctionReason::InvalidBlock(( - new_block.header.height, - new_block.hash, + new_block.kernel.header.height, + new_block.hash(), ))) .await?; bail!("Failed to validate block: invalid block"); } else { info!( "Block with height {} is valid. mined: {}", - new_block.header.height, - crate::utc_timestamp_to_localtime(new_block.header.timestamp.value()) + new_block.kernel.header.height, + crate::utc_timestamp_to_localtime(new_block.kernel.header.timestamp.value()) .to_string() ); } @@ -159,7 +159,7 @@ impl PeerLoopHandler { // Send the new blocks to the main thread which handles the state update // and storage to the database. - let new_block_height = received_blocks.last().unwrap().header.height; + let new_block_height = received_blocks.last().unwrap().kernel.header.height; self.to_main_tx .send(PeerThreadToMain::NewBlocks(received_blocks)) .await?; @@ -186,7 +186,7 @@ impl PeerLoopHandler { >::Error: std::error::Error + Sync + Send + 'static, ::Error: std::error::Error, { - let parent_digest = received_block.header.prev_block_digest; + let parent_digest = received_block.kernel.header.prev_block_digest; debug!("Fetching parent block"); let parent_block = self .global_state_lock @@ -204,7 +204,7 @@ impl PeerLoopHandler { "not found".to_string() } ); - let parent_height = received_block.header.height.previous(); + let parent_height = received_block.kernel.header.height.previous(); // If parent is not known, request the parent, and add the current to the peer fork resolution list if parent_block.is_none() && parent_height > BlockHeight::genesis() { @@ -220,10 +220,11 @@ impl PeerLoopHandler { .fork_reconciliation_blocks .last() .unwrap() + .kernel .header .height .previous() - == received_block.header.height + == received_block.kernel.header.height && peer_state.fork_reconciliation_blocks.len() + 1 < self .global_state_lock @@ -237,9 +238,9 @@ impl PeerLoopHandler { // Blocks received out of order. Or more than allowed received without // going into sync mode. Give up on block resolution attempt. self.punish(PeerSanctionReason::ForkResolutionError(( - received_block.header.height, + received_block.kernel.header.height, peer_state.fork_reconciliation_blocks.len() as u16, - received_block.hash, + received_block.hash(), ))) .await?; warn!( @@ -277,14 +278,14 @@ impl PeerLoopHandler { // TODO: This assert should be replaced with something to punish or disconnect // from a peer instead. It can be used by a malevolent peer to crash peer nodes. let mut new_blocks_sorted_check = new_blocks.clone(); - new_blocks_sorted_check.sort_by(|a, b| a.header.height.cmp(&b.header.height)); + new_blocks_sorted_check.sort_by(|a, b| a.kernel.header.height.cmp(&b.kernel.header.height)); assert_eq!( new_blocks_sorted_check, new_blocks, "Block list in fork resolution must be sorted. Got blocks in this order: {}", new_blocks .iter() - .map(|b| b.header.height.to_string()) + .map(|b| b.kernel.header.height.to_string()) .join(", ") ); @@ -404,12 +405,13 @@ impl PeerLoopHandler { .await .chain .light_state() - .header() + .kernel + .header .proof_of_work_family - < block.header.proof_of_work_family; + < block.kernel.header.proof_of_work_family; let reconciliation_ongoing = match peer_state_info.fork_reconciliation_blocks.last() { - Some(last_block) => last_block.header.prev_block_digest == block.hash, + Some(last_block) => last_block.kernel.header.prev_block_digest == block.hash(), None => false, }; @@ -422,7 +424,7 @@ impl PeerLoopHandler { } else { info!( "Got non-canonical block from peer, height: {}, PoW family: {:?}", - new_block_height, block.header.proof_of_work_family, + new_block_height, block.kernel.header.proof_of_work_family, ); } Ok(false) @@ -448,12 +450,15 @@ impl PeerLoopHandler { let global_state = self.global_state_lock.lock_guard().await; - let tip_header = global_state.chain.light_state().header(); + let tip_header = &global_state.chain.light_state().kernel.header; if global_state .chain .archival_state() - .block_belongs_to_canonical_chain(&block_candidate.header, tip_header) + .block_belongs_to_canonical_chain( + &block_candidate.kernel.header, + tip_header, + ) .await { peers_most_canonical_block = Some(block_candidate); @@ -484,13 +489,13 @@ impl PeerLoopHandler { / 2, ); let global_state = self.global_state_lock.lock_guard().await; - let tip_header = global_state.chain.light_state().header(); + let tip_header = &global_state.chain.light_state().kernel.header; let responded_batch_size = cmp::max(responded_batch_size, MINIMUM_BLOCK_BATCH_SIZE); let mut returned_blocks: Vec = Vec::with_capacity(responded_batch_size); - let mut parent_block_header: BlockHeader = peers_most_canonical_block.header; + let mut parent_block_header: BlockHeader = peers_most_canonical_block.kernel.header; while returned_blocks.len() < responded_batch_size { let children = global_state .chain @@ -508,7 +513,7 @@ impl PeerLoopHandler { if global_state .chain .archival_state() - .block_belongs_to_canonical_chain(&child, tip_header) + .block_belongs_to_canonical_chain(&child, &tip_header) .await { canonical = child; @@ -586,7 +591,7 @@ impl PeerLoopHandler { // Convert all blocks to Block objects debug!( "Found own block of height {} to match received batch", - most_canonical_own_block_match.header.height + most_canonical_own_block_match.kernel.header.height ); let received_blocks: Vec = t_blocks.into_iter().map(|x| x.into()).collect(); @@ -600,14 +605,15 @@ impl PeerLoopHandler { debug!("Got BlockNotificationRequest"); peer.send(PeerMessage::BlockNotification( - (self + (&self .global_state_lock .lock_guard() .await .chain .light_state() - .header()) - .into(), + .kernel + .header) + .into(), )) .await?; @@ -626,7 +632,8 @@ impl PeerLoopHandler { .await .chain .light_state() - .header() + .kernel + .header .proof_of_work_family < block_notification.proof_of_work_family; @@ -697,7 +704,7 @@ impl PeerLoopHandler { let mut canonical_chain_block_header = block_headers[0].clone(); if block_headers.len() > 1 { let global_state = self.global_state_lock.lock_guard().await; - let tip_header = global_state.chain.light_state().header(); + let tip_header = &global_state.chain.light_state().kernel.header; for block_header in block_headers { if global_state .chain @@ -768,6 +775,7 @@ impl PeerLoopHandler { .await .chain .light_state() + .kernel .body .next_mutator_set_accumulator, ); @@ -888,7 +896,7 @@ impl PeerLoopHandler { MainToPeerThread::Block(block) => { // We don't currently differentiate whether a new block came from a peer, or from our // own miner. It's always shared through this logic. - let new_block_height = block.header.height; + let new_block_height = block.kernel.header.height; if new_block_height > peer_state_info.highest_shared_block_height { debug!("Sending PeerMessage::BlockNotification"); peer_state_info.highest_shared_block_height = new_block_height; @@ -1130,7 +1138,8 @@ impl PeerLoopHandler { .await .chain .light_state() - .header() + .kernel + .header .proof_of_work_family { peer.send(PeerMessage::BlockNotificationRequest).await?; @@ -1161,9 +1170,7 @@ mod peer_loop_tests { use crate::{ config_models::{cli_args, network::Network}, - models::{ - blockchain::shared::Hash, peer::TransactionNotification, state::wallet::WalletSecret, - }, + models::{peer::TransactionNotification, state::wallet::WalletSecret}, tests::shared::{ add_block, get_dummy_peer_connection_data_genesis, get_dummy_socket_address, get_test_genesis_setup, make_mock_block_with_invalid_pow, @@ -1267,8 +1274,7 @@ mod peer_loop_tests { .archival_state() .get_latest_block() .await; - different_genesis_block.header.nonce[2].increment(); - different_genesis_block.hash = Hash::hash(&different_genesis_block.header); + different_genesis_block.kernel.header.nonce[2].increment(); let a_wallet_secret = WalletSecret::new_random(); let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); let (block_1_with_different_genesis, _, _) = @@ -1518,7 +1524,10 @@ mod peer_loop_tests { drop(global_state_mut); let mut mock = Mock::new(vec![ - Action::Read(PeerMessage::BlockRequestBatch(vec![genesis_block.hash], 14)), + Action::Read(PeerMessage::BlockRequestBatch( + vec![genesis_block.hash()], + 14, + )), Action::Write(PeerMessage::BlockResponseBatch(vec![ block_1.clone().into(), block_2_a.clone().into(), @@ -1543,7 +1552,7 @@ mod peer_loop_tests { // Peer knows block 2_b, verify that canonical chain with 2_a is returned mock = Mock::new(vec![ Action::Read(PeerMessage::BlockRequestBatch( - vec![block_2_b.hash, block_1.hash, genesis_block.hash], + vec![block_2_b.hash(), block_1.hash(), genesis_block.hash()], 14, )), Action::Write(PeerMessage::BlockResponseBatch(vec![ @@ -1714,7 +1723,7 @@ mod peer_loop_tests { let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_2.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_1.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_1.hash())), Action::Read(PeerMessage::Block(Box::new(block_1.clone().into()))), Action::Read(PeerMessage::Bye), ]); @@ -1739,10 +1748,10 @@ mod peer_loop_tests { match to_main_rx1.recv().await { Some(PeerThreadToMain::NewBlocks(blocks)) => { - if blocks[0].hash != block_1.hash { + if blocks[0].hash() != block_1.hash() { bail!("1st received block by main loop must be block 1"); } - if blocks[1].hash != block_2.hash { + if blocks[1].hash() != block_2.hash() { bail!("2nd received block by main loop must be block 2"); } } @@ -1801,7 +1810,7 @@ mod peer_loop_tests { let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_4.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_3.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_3.hash())), Action::Read(PeerMessage::Block(Box::new(block_3.clone().into()))), Action::Read(PeerMessage::Bye), ]); @@ -1879,9 +1888,9 @@ mod peer_loop_tests { let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_4.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_3.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_3.hash())), Action::Read(PeerMessage::Block(Box::new(block_3.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_2.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_2.hash())), Action::Read(PeerMessage::Block(Box::new(block_2.clone().into()))), Action::Read(PeerMessage::Bye), ]); @@ -1906,13 +1915,13 @@ mod peer_loop_tests { match to_main_rx1.recv().await { Some(PeerThreadToMain::NewBlocks(blocks)) => { - if blocks[0].hash != block_2.hash { + if blocks[0].hash() != block_2.hash() { bail!("1st received block by main loop must be block 1"); } - if blocks[1].hash != block_3.hash { + if blocks[1].hash() != block_3.hash() { bail!("2nd received block by main loop must be block 2"); } - if blocks[2].hash != block_4.hash { + if blocks[2].hash() != block_4.hash() { bail!("3rd received block by main loop must be block 3"); } } @@ -1954,9 +1963,9 @@ mod peer_loop_tests { let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_3.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_2.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_2.hash())), Action::Read(PeerMessage::Block(Box::new(block_2.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_1.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_1.hash())), Action::Read(PeerMessage::Block(Box::new(block_1.clone().into()))), Action::Read(PeerMessage::Bye), ]); @@ -1981,13 +1990,13 @@ mod peer_loop_tests { match to_main_rx1.recv().await { Some(PeerThreadToMain::NewBlocks(blocks)) => { - if blocks[0].hash != block_1.hash { + if blocks[0].hash() != block_1.hash() { bail!("1st received block by main loop must be block 1"); } - if blocks[1].hash != block_2.hash { + if blocks[1].hash() != block_2.hash() { bail!("2nd received block by main loop must be block 2"); } - if blocks[2].hash != block_3.hash { + if blocks[2].hash() != block_3.hash() { bail!("3rd received block by main loop must be block 3"); } } @@ -2039,9 +2048,9 @@ mod peer_loop_tests { let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_4.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_3.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_3.hash())), Action::Read(PeerMessage::Block(Box::new(block_3.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_2.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_2.hash())), // // Now make the interruption of the block reconciliation process Action::Read(PeerMessage::BlockNotification(block_5.clone().into())), @@ -2055,7 +2064,9 @@ mod peer_loop_tests { // Note that we cannot anticipate the response, as only the main // thread writes to the database. And the database needs to be updated // for the handling of block 5 to be done correctly. - Action::Write(PeerMessage::BlockRequestByHeight(block_5.header.height)), + Action::Write(PeerMessage::BlockRequestByHeight( + block_5.kernel.header.height, + )), Action::Read(PeerMessage::Bye), ]); @@ -2079,13 +2090,13 @@ mod peer_loop_tests { match to_main_rx1.recv().await { Some(PeerThreadToMain::NewBlocks(blocks)) => { - if blocks[0].hash != block_2.hash { + if blocks[0].hash() != block_2.hash() { bail!("1st received block by main loop must be block 1"); } - if blocks[1].hash != block_3.hash { + if blocks[1].hash() != block_3.hash() { bail!("2nd received block by main loop must be block 2"); } - if blocks[2].hash != block_4.hash { + if blocks[2].hash() != block_4.hash() { bail!("3rd received block by main loop must be block 3"); } } @@ -2149,9 +2160,9 @@ mod peer_loop_tests { ]; let mock = Mock::new(vec![ Action::Read(PeerMessage::Block(Box::new(block_4.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_3.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_3.hash())), Action::Read(PeerMessage::Block(Box::new(block_3.clone().into()))), - Action::Write(PeerMessage::BlockRequestByHash(block_2.hash)), + Action::Write(PeerMessage::BlockRequestByHash(block_2.hash())), // // Now make the interruption of the block reconciliation process Action::Read(PeerMessage::PeerListRequest), @@ -2180,13 +2191,13 @@ mod peer_loop_tests { // Verify that blocks are sent to `main_loop` in expected ordering match to_main_rx1.recv().await { Some(PeerThreadToMain::NewBlocks(blocks)) => { - if blocks[0].hash != block_2.hash { + if blocks[0].hash() != block_2.hash() { bail!("1st received block by main loop must be block 1"); } - if blocks[1].hash != block_3.hash { + if blocks[1].hash() != block_3.hash() { bail!("2nd received block by main loop must be block 2"); } - if blocks[2].hash != block_4.hash { + if blocks[2].hash() != block_4.hash() { bail!("3rd received block by main loop must be block 3"); } } diff --git a/src/rpc_server.rs b/src/rpc_server.rs index 0eaace24d..253dd9971 100644 --- a/src/rpc_server.rs +++ b/src/rpc_server.rs @@ -204,6 +204,7 @@ impl RPC for NeptuneRPCServer { .await .chain .light_state() + .kernel .header .height } @@ -338,6 +339,7 @@ impl RPC for NeptuneRPCServer { .await .chain .light_state() + .kernel .header .clone() } diff --git a/src/tests/shared.rs b/src/tests/shared.rs index c7338d428..60d383b98 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -147,7 +147,7 @@ pub fn get_dummy_latest_block( }; let latest_block_info: LatestBlockInfo = block.clone().into(); - let block_header = block.header.clone(); + let block_header = block.kernel.header.clone(); ( block, latest_block_info, @@ -261,8 +261,8 @@ pub async fn add_block_to_light_state( light_state: &mut LightState, new_block: Block, ) -> Result<()> { - let previous_pow_family = light_state.header.proof_of_work_family; - if previous_pow_family < new_block.header.proof_of_work_family { + let previous_pow_family = light_state.kernel.header.proof_of_work_family; + if previous_pow_family < new_block.kernel.header.proof_of_work_family { light_state.set_block(new_block); } else { panic!("Attempted to add to light state an older block than the current light state block"); @@ -317,13 +317,13 @@ pub fn unit_test_data_directory(network: Network) -> Result { /// Helper function for tests to update state with a new block pub async fn add_block(state: &mut GlobalState, new_block: Block) -> Result<()> { - let previous_pow_family = state.chain.light_state().header.proof_of_work_family; + let previous_pow_family = state.chain.light_state().kernel.header.proof_of_work_family; state .chain .archival_state_mut() .write_block(&new_block, Some(previous_pow_family)) .await?; - if previous_pow_family < new_block.header.proof_of_work_family { + if previous_pow_family < new_block.kernel.header.proof_of_work_family { state.chain.light_state_mut().set_block(new_block); } @@ -894,7 +894,7 @@ pub fn make_mock_block( block_timestamp: Option, coinbase_beneficiary: generation_address::ReceivingAddress, ) -> (Block, Utxo, Digest) { - let new_block_height: BlockHeight = previous_block.header.height.next(); + let new_block_height: BlockHeight = previous_block.kernel.header.height.next(); // Build coinbase UTXO and associated data let lock_script = coinbase_beneficiary.lock_script(); @@ -903,7 +903,11 @@ pub fn make_mock_block( let coinbase_output_randomness: Digest = Digest::new(random_elements_array()); let receiver_digest: Digest = coinbase_beneficiary.privacy_digest; - let mut next_mutator_set = previous_block.body.next_mutator_set_accumulator.clone(); + let mut next_mutator_set = previous_block + .kernel + .body + .next_mutator_set_accumulator + .clone(); let previous_mutator_set = next_mutator_set.clone(); let coinbase_digest: Digest = Hash::hash(&coinbase_utxo); @@ -913,7 +917,7 @@ pub fn make_mock_block( let block_timestamp = match block_timestamp { Some(ts) => ts, - None => previous_block.header.timestamp.value() + TARGET_BLOCK_INTERVAL, + None => previous_block.kernel.header.timestamp.value() + TARGET_BLOCK_INTERVAL, }; let tx_kernel = TransactionKernel { @@ -949,11 +953,10 @@ pub fn make_mock_block( next_mutator_set_accumulator: next_mutator_set.clone(), previous_mutator_set_accumulator: previous_mutator_set, - stark_proof: vec![], }; - let block_target_difficulty = previous_block.header.difficulty; - let pow_line = previous_block.header.proof_of_work_line + block_target_difficulty; + let block_target_difficulty = previous_block.kernel.header.difficulty; + let pow_line = previous_block.kernel.header.proof_of_work_line + block_target_difficulty; let pow_family = pow_line; let zero = BFieldElement::zero(); let target_difficulty = Block::difficulty_control(previous_block, block_timestamp); @@ -961,7 +964,7 @@ pub fn make_mock_block( version: zero, height: new_block_height, mutator_set_hash: next_mutator_set.hash(), - prev_block_digest: previous_block.hash, + prev_block_digest: previous_block.hash(), timestamp: block_body.transaction.kernel.timestamp, nonce: [zero, zero, zero], max_block_size: 1_000_000, @@ -973,7 +976,7 @@ pub fn make_mock_block( }; ( - Block::new(block_header, block_body), + Block::new(block_header, block_body, None), coinbase_utxo, coinbase_output_randomness, ) From 5ac6e8418ad10ed93e07a4f2d01a5c8d2a80db40 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Fri, 26 Jan 2024 17:35:37 +0100 Subject: [PATCH 07/29] refactor: wip modify block structure --- Cargo.lock | 156 ++++++++++-------- src/models/blockchain/block/block_body.rs | 50 +++++- src/models/blockchain/block/block_header.rs | 37 +++++ src/models/blockchain/block/block_kernel.rs | 30 +++- src/models/blockchain/block/mod.rs | 58 +++---- src/models/blockchain/transaction/mod.rs | 20 +-- .../transaction/transaction_kernel.rs | 52 ++---- .../validity/kernel_to_lock_scripts.rs | 1 + .../validity/kernel_to_type_scripts.rs | 1 + .../transaction/validity/lockscripts_halt.rs | 5 +- .../validity/removal_records_integrity.rs | 1 + .../tasm/removal_records_integrity.rs | 3 +- .../tasm/transaction_kernel_mast_hash.rs | 2 + .../transaction/validity/typescripts_halt.rs | 5 +- src/models/consensus/mast_hash.rs | 48 ++++++ src/models/consensus/mod.rs | 2 + src/models/state/wallet/mod.rs | 6 +- src/models/state/wallet/wallet_state.rs | 76 ++++----- src/peer_loop.rs | 2 +- src/tests/shared.rs | 4 +- 20 files changed, 345 insertions(+), 214 deletions(-) create mode 100644 src/models/consensus/mast_hash.rs diff --git a/Cargo.lock b/Cargo.lock index 9288e1096..dc03b0d13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,9 +102,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -342,9 +342,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "blake3" @@ -454,23 +454,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -479,15 +479,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.15" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c12ed66a79a555082f595f7eb980d08669de95009dd4b3d61168c573ebe38fc9" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.15" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4645eab3431e5a8403a96bea02506a8b35d28cd0f0330977dd5d22f9c84f43" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -528,9 +528,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.6" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd" +checksum = "df631ae429f6613fcd3a7c1adbdb65f637271e561b03680adaa6573015dfb106" dependencies = [ "clap", ] @@ -733,7 +733,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "crossterm_winapi", "libc", "mio", @@ -752,6 +752,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -855,9 +861,9 @@ dependencies = [ [[package]] name = "divan" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf174e1957cfadab3bcb040afb210ea8b7c2ffc094eb88b750be61fc601835e" +checksum = "5398159ee27f2b123d89b856bad61725442f37df5fb98c30cd570c318d594aee" dependencies = [ "cfg-if", "clap", @@ -869,9 +875,9 @@ dependencies = [ [[package]] name = "divan-macros" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510ef69263e119c8ec0213761e397d26a3f6d35e13a454549508446d3578ddc0" +checksum = "5092f66eb3563a01e85552731ae82c04c934ff4efd7ad1a0deae7b948f4b3ec4" dependencies = [ "proc-macro2", "quote", @@ -1186,9 +1192,13 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -1227,9 +1237,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "hex" @@ -1432,18 +1442,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -1509,16 +1519,16 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -1585,9 +1595,9 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -1999,18 +2009,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -2134,9 +2144,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2251,7 +2261,7 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e2e4cd95294a85c3b4446e63ef054eea43e0205b1fd60120c16b74ff7ff96ad" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cassowary", "crossterm", "indoc", @@ -2270,9 +2280,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -2280,9 +2290,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -2310,13 +2320,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -2331,9 +2341,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -2382,11 +2392,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -2545,9 +2555,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -2653,9 +2663,9 @@ dependencies = [ [[package]] name = "tarpc-plugins" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0b234b43836d3ee3bcc318eeaf38ec27f28e398cb9d700b62e9e47f2744536" +checksum = "ad8302bea2fb8a2b01b025d23414b0b4ed32a783b95e5d818c3320a8bc4baada" dependencies = [ "proc-macro2", "quote", @@ -2670,7 +2680,7 @@ dependencies = [ "anyhow", "derive_tasm_object", "hex", - "itertools 0.10.5", + "itertools 0.12.0", "num", "num-traits", "rand", @@ -3202,9 +3212,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3212,9 +3222,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -3227,9 +3237,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3237,9 +3247,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -3250,15 +3260,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index 72fd13737..7a25a0943 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -1,16 +1,62 @@ +use crate::models::consensus::mast_hash::{HasDiscriminant, MastHash}; use crate::prelude::twenty_first; use get_size::GetSize; use serde::{Deserialize, Serialize}; +use tasm_lib::twenty_first::shared_math::b_field_element::BFieldElement; +use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use twenty_first::shared_math::bfield_codec::BFieldCodec; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::Transaction; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; +enum BlockBodyField { + Transaction, + MutatorSetAccumulator, + LockFreeMmrAccumulator, + BlockMmrAccumulator, +} + +impl HasDiscriminant for BlockBodyField { + fn discriminant(&self) -> usize { + match self { + Transaction => 0, + MutatorSetAccumulator => 1, + LockFreeMmrAccumulator => 2, + BlockMmrAccumulator => 3, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BFieldCodec, GetSize)] pub struct BlockBody { + /// Every block contains exactly one transaction, which represents the merger of all + /// broadcasted transactions that the miner decided to confirm. pub transaction: Transaction, - pub next_mutator_set_accumulator: MutatorSetAccumulator, - pub previous_mutator_set_accumulator: MutatorSetAccumulator, + + /// The mutator set accumulator represents the UTXO set. It is simultaneously an + /// accumulator (=> compact representation and membership proofs) and an anonymity + /// construction (=> outputs from one transaction do not look like inputs to another). + pub mutator_set_accumulator: MutatorSetAccumulator, + + /// Lock-free UTXOs do not come with lock scripts and do not live in the mutator set. + pub lock_free_mmr_accumulator: MmrAccumulator, + + /// All blocks live in an MMR, so that we can efficiently prove that a given block + /// lives on the line between the tip and genesis. + pub block_mmr_accumulator: MmrAccumulator, +} + +impl MastHash for BlockBody { + type FieldEnum = BlockBodyField; + + fn mast_sequences(&self) -> Vec> { + vec![ + self.transaction.encode(), + self.mutator_set_accumulator.encode(), + self.lock_free_mmr_accumulator.encode(), + self.block_mmr_accumulator.encode(), + ] + } } diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 9468bad61..ca4ffd5e4 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -1,8 +1,12 @@ +use crate::models::blockchain::shared::Hash; use crate::prelude::twenty_first; use get_size::GetSize; use serde::{Deserialize, Serialize}; use std::fmt::Display; +use tasm_lib::twenty_first::prelude::AlgebraicHasher; +use tasm_lib::twenty_first::util_types::merkle_tree::{CpuParallel, MerkleTree}; +use tasm_lib::twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; use twenty_first::shared_math::bfield_codec::BFieldCodec; use twenty_first::shared_math::digest::Digest; @@ -62,6 +66,39 @@ impl Display for BlockHeader { } } +impl BlockHeader { + fn mast_sequences(&self) -> [Vec; 16] { + [ + self.version.encode(), + self.height.encode(), + self.mutator_set_hash.encode(), + self.prev_block_digest.encode(), + self.timestamp.encode(), + self.nonce.encode(), + self.max_block_size.encode(), + self.proof_of_work_line.encode(), + self.proof_of_work_family.encode(), + self.difficulty.encode(), + self.block_body_merkle_root.encode(), + self.uncles.encode(), + vec![], + vec![], + vec![], + vec![], + ] + } + + fn merkle_tree(&self) -> MerkleTree { + let sequences = self.mast_sequences(); + let digests = sequences.map(|seq| Hash::hash_varlen(&seq)); + CpuParallel::from_digests(&digests).unwrap() + } + + pub(crate) fn mast_hash(&self) -> Digest { + self.merkle_tree().root() + } +} + #[cfg(test)] mod block_header_tests { use rand::{thread_rng, Rng, RngCore}; diff --git a/src/models/blockchain/block/block_kernel.rs b/src/models/blockchain/block/block_kernel.rs index 42e925af8..b0e17500e 100644 --- a/src/models/blockchain/block/block_kernel.rs +++ b/src/models/blockchain/block/block_kernel.rs @@ -1,6 +1,8 @@ use get_size::GetSize; use serde::{Deserialize, Serialize}; -use tasm_lib::twenty_first::shared_math::{bfield_codec::BFieldCodec, tip5::Digest}; +use tasm_lib::twenty_first::shared_math::bfield_codec::BFieldCodec; + +use crate::models::consensus::mast_hash::{HasDiscriminant, MastHash}; use super::{block_body::BlockBody, block_header::BlockHeader}; @@ -10,8 +12,28 @@ pub struct BlockKernel { pub header: BlockHeader, pub body: BlockBody, } -impl BlockKernel { - pub(crate) fn mast_hash(&self) -> Digest { - todo!() + +pub enum BlockKernelField { + Header, + Body, +} + +impl HasDiscriminant for BlockKernelField { + fn discriminant(&self) -> usize { + match self { + Header => 0, + Body => 1, + } + } +} + +impl MastHash for BlockKernel { + type FieldEnum = BlockKernelField; + + fn mast_sequences(&self) -> Vec> { + vec![ + self.header.mast_hash().encode(), + self.body.mast_hash().encode(), + ] } } diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 0eb006fa3..d9cfc12be 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::twenty_first; use get_size::GetSize; @@ -8,6 +9,7 @@ use num_traits::{abs, Zero}; use serde::{Deserialize, Serialize}; use std::cmp::max; use tasm_lib::triton_vm::proof::Proof; +use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use twenty_first::shared_math::bfield_codec::BFieldCodec; use tracing::{debug, error, warn}; @@ -117,8 +119,8 @@ impl Block { } pub fn genesis_block() -> Self { - let empty_mutator_set = MutatorSetAccumulator::default(); - let mut genesis_mutator_set = MutatorSetAccumulator::default(); + let empty_mutator_set = MutatorSetAccumulator::::default(); + let mut genesis_mutator_set = MutatorSetAccumulator::::default(); let mut ms_update = MutatorSetUpdate::default(); let premine_distribution = Self::premine_distribution(); @@ -164,8 +166,9 @@ impl Block { let body: BlockBody = BlockBody { transaction: genesis_coinbase_tx, - next_mutator_set_accumulator: genesis_mutator_set.clone(), - previous_mutator_set_accumulator: empty_mutator_set, + mutator_set_accumulator: genesis_mutator_set.clone(), + block_mmr_accumulator: MmrAccumulator::new(vec![]), + lock_free_mmr_accumulator: MmrAccumulator::new(vec![]), }; let header: BlockHeader = BlockHeader { @@ -221,8 +224,7 @@ impl Block { // accumulate let additions = new_transaction.kernel.outputs.clone(); let removals = new_transaction.kernel.inputs.clone(); - let mut next_mutator_set_accumulator = - self.kernel.body.previous_mutator_set_accumulator.clone(); + let mut next_mutator_set_accumulator = self.kernel.body.mutator_set_accumulator.clone(); let mutator_set_update = MutatorSetUpdate::new(removals, additions); @@ -233,12 +235,9 @@ impl Block { let block_body: BlockBody = BlockBody { transaction: new_transaction, - next_mutator_set_accumulator: next_mutator_set_accumulator.clone(), - previous_mutator_set_accumulator: self - .kernel - .body - .previous_mutator_set_accumulator - .clone(), + mutator_set_accumulator: next_mutator_set_accumulator.clone(), + lock_free_mmr_accumulator: self.kernel.body.lock_free_mmr_accumulator, + block_mmr_accumulator: self.kernel.body.block_mmr_accumulator, }; let block_header = BlockHeader { @@ -274,8 +273,7 @@ impl Block { // a) Block height is previous plus one // b) Block header points to previous block // c) Block timestamp is greater than previous block timestamp - // d) Next mutator set of previous block matches previous MS of current block - // e) Target difficulty was adjusted correctly + // d) Target difficulty, and other control parameters, were adjusted correctly // 1. The transaction is valid. // 1'. All transactions are valid. // a) verify that MS membership proof is valid, done against `previous_mutator_set_accumulator`, @@ -307,15 +305,7 @@ impl Block { return false; } - // 0.d) Next mutator set of previous block matches previous MS of current block - if previous_block.kernel.body.next_mutator_set_accumulator - != block_copy.kernel.body.previous_mutator_set_accumulator - { - warn!("Value for previous mutator set does not match previous block"); - return false; - } - - // 0.e) Target difficulty was updated correctly + // 0.d) Target difficulty, and other control parameters, were updated correctly if block_copy.kernel.header.difficulty != Self::difficulty_control(previous_block, block_copy.kernel.header.timestamp.value()) { @@ -326,10 +316,10 @@ impl Block { // 1.b) Verify validity of removal records: That their MMR MPs match the SWBF, and // that at least one of their listed indices is absent. for removal_record in block_copy.kernel.body.transaction.kernel.inputs.iter() { - if !block_copy + if !previous_block .kernel .body - .previous_mutator_set_accumulator + .mutator_set_accumulator .kernel .can_remove(removal_record) { @@ -363,11 +353,7 @@ impl Block { block_copy.kernel.body.transaction.kernel.inputs.clone(), block_copy.kernel.body.transaction.kernel.outputs.clone(), ); - let mut ms = block_copy - .kernel - .body - .previous_mutator_set_accumulator - .clone(); + let mut ms = previous_block.kernel.body.mutator_set_accumulator.clone(); let ms_update_result = mutator_set_update.apply(&mut ms); match ms_update_result { Ok(()) => (), @@ -379,11 +365,11 @@ impl Block { // Verify that the locally constructed mutator set matches that in the received // block's body. - if ms.hash() != block_copy.kernel.body.next_mutator_set_accumulator.hash() { + if ms.hash() != block_copy.kernel.body.mutator_set_accumulator.hash() { warn!("Reported mutator set does not match calculated object."); debug!( "From Block\n{:?}. \n\n\nCalculated\n{:?}", - block_copy.kernel.body.next_mutator_set_accumulator, ms + block_copy.kernel.body.mutator_set_accumulator, ms ); return false; } @@ -518,12 +504,12 @@ mod block_tests { }, tests::shared::{get_mock_global_state, make_mock_block}, }; + use tasm_lib::twenty_first::util_types::emojihash_trait::Emojihash; use super::*; use rand::random; use tracing_test::traced_test; - use twenty_first::util_types::emojihash_trait::Emojihash; #[traced_test] #[tokio::test] @@ -571,14 +557,14 @@ mod block_tests { assert!( block_1.is_valid(&genesis_block), "Block 1 must be valid after adding a transaction; previous mutator set hash: {} and next mutator set hash: {}", - block_1.kernel + genesis_block.kernel .body - .previous_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash(), block_1.kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash() ); diff --git a/src/models/blockchain/transaction/mod.rs b/src/models/blockchain/transaction/mod.rs index 73aed9852..15e199340 100644 --- a/src/models/blockchain/transaction/mod.rs +++ b/src/models/blockchain/transaction/mod.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::{triton_vm, twenty_first}; pub mod amount; @@ -86,12 +87,12 @@ impl Transaction { /// compatibility with a new block. Note that for SingleProof witnesses, this will /// invalidate the proof, requiring an update. For LinkedProofs or PrimitiveWitness /// witnesses the witness data can be and is updated. - pub fn update_mutator_set_records(&mut self, block: &Block) -> Result<()> { - let mut msa_state: MutatorSetAccumulator = block - .kernel - .body - .previous_mutator_set_accumulator - .to_owned(); + pub fn update_mutator_set_records( + &mut self, + previous_mutator_set_accumulator: &MutatorSetAccumulator, + block: &Block, + ) -> Result<()> { + let mut msa_state: MutatorSetAccumulator = previous_mutator_set_accumulator.clone(); let block_addition_records: Vec = block.kernel.body.transaction.kernel.outputs.clone(); let mut transaction_removal_records: Vec> = self.kernel.inputs.clone(); @@ -160,12 +161,7 @@ impl Transaction { } // Sanity check of block validity - let block_msa_hash = block - .kernel - .body - .next_mutator_set_accumulator - .clone() - .hash(); + let block_msa_hash = block.kernel.body.mutator_set_accumulator.clone().hash(); assert_eq!( msa_state.hash(), block_msa_hash, diff --git a/src/models/blockchain/transaction/transaction_kernel.rs b/src/models/blockchain/transaction/transaction_kernel.rs index 12f1c294a..c1c8b4feb 100644 --- a/src/models/blockchain/transaction/transaction_kernel.rs +++ b/src/models/blockchain/transaction/transaction_kernel.rs @@ -1,17 +1,15 @@ -use crate::prelude::twenty_first; +use crate::{ + models::consensus::mast_hash::{HasDiscriminant, MastHash}, + prelude::twenty_first, +}; use get_size::GetSize; use itertools::Itertools; use rand::{rngs::StdRng, Rng, RngCore, SeedableRng}; use serde::{Deserialize, Serialize}; use tasm_lib::structure::tasm_object::TasmObject; -use twenty_first::{ - shared_math::{b_field_element::BFieldElement, bfield_codec::BFieldCodec, tip5::Digest}, - util_types::{ - algebraic_hasher::AlgebraicHasher, - merkle_tree::{CpuParallel, MerkleTree}, - merkle_tree_maker::MerkleTreeMaker, - }, +use twenty_first::shared_math::{ + b_field_element::BFieldElement, bfield_codec::BFieldCodec, tip5::Digest, }; use super::{amount::pseudorandom_amount, Amount, PublicAnnouncement}; @@ -57,8 +55,8 @@ pub enum TransactionKernelField { MutatorSetHash, } -impl TransactionKernelField { - pub fn discriminant(&self) -> usize { +impl HasDiscriminant for TransactionKernelField { + fn discriminant(&self) -> usize { match self { TransactionKernelField::InputUtxos => 0, TransactionKernelField::OutputUtxos => 1, @@ -71,9 +69,11 @@ impl TransactionKernelField { } } -impl TransactionKernel { +impl MastHash for TransactionKernel { + type FieldEnum = TransactionKernelField; + /// Return the sequences (= leaf preimages) of the kernel Merkle tree. - pub fn mast_sequences(&self) -> Vec> { + fn mast_sequences(&self) -> Vec> { let input_utxos_sequence = self.inputs.encode(); let output_utxos_sequence = self.outputs.encode(); @@ -98,34 +98,6 @@ impl TransactionKernel { mutator_set_hash_sequence, ] } - - fn merkle_tree(&self) -> MerkleTree { - // get a sequence of BFieldElements for each field - let sequences = self.mast_sequences(); - - let mut mt_leafs = sequences - .iter() - .map(|seq| Hash::hash_varlen(seq)) - .collect_vec(); - - // pad until power of two - while mt_leafs.len() & (mt_leafs.len() - 1) != 0 { - mt_leafs.push(Digest::default()); - } - - // compute Merkle tree and return hash - >::from_digests(&mt_leafs).unwrap() - } - - pub fn mast_path(&self, field: TransactionKernelField) -> Vec { - self.merkle_tree() - .authentication_structure(&[field.discriminant()]) - .unwrap() - } - - pub fn mast_hash(&self) -> Digest { - self.merkle_tree().root() - } } pub fn pseudorandom_option(seed: [u8; 32], thing: T) -> Option { diff --git a/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs b/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs index 9a9eec750..ef21015f1 100644 --- a/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs +++ b/src/models/blockchain/transaction/validity/kernel_to_lock_scripts.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::{triton_vm, twenty_first}; use crate::models::blockchain::transaction::TransactionPrimitiveWitness; diff --git a/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs b/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs index 8d54f100c..0ccdfdb6f 100644 --- a/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs +++ b/src/models/blockchain/transaction/validity/kernel_to_type_scripts.rs @@ -1,4 +1,5 @@ use crate::models::blockchain::transaction::transaction_kernel::TransactionKernel; +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::{triton_vm, twenty_first}; use crate::models::blockchain::transaction::utxo::TypeScript; diff --git a/src/models/blockchain/transaction/validity/lockscripts_halt.rs b/src/models/blockchain/transaction/validity/lockscripts_halt.rs index 6ed1ae109..5f075b2b7 100644 --- a/src/models/blockchain/transaction/validity/lockscripts_halt.rs +++ b/src/models/blockchain/transaction/validity/lockscripts_halt.rs @@ -1,4 +1,7 @@ -use crate::prelude::{triton_vm, twenty_first}; +use crate::{ + models::consensus::mast_hash::MastHash, + prelude::{triton_vm, twenty_first}, +}; use get_size::GetSize; use itertools::Itertools; diff --git a/src/models/blockchain/transaction/validity/removal_records_integrity.rs b/src/models/blockchain/transaction/validity/removal_records_integrity.rs index 458efc46b..e5b6b8dcf 100644 --- a/src/models/blockchain/transaction/validity/removal_records_integrity.rs +++ b/src/models/blockchain/transaction/validity/removal_records_integrity.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::{triton_vm, twenty_first}; use field_count::FieldCount; diff --git a/src/models/blockchain/transaction/validity/tasm/removal_records_integrity.rs b/src/models/blockchain/transaction/validity/tasm/removal_records_integrity.rs index bdd1922c9..156d4109f 100644 --- a/src/models/blockchain/transaction/validity/tasm/removal_records_integrity.rs +++ b/src/models/blockchain/transaction/validity/tasm/removal_records_integrity.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::{triton_vm, twenty_first}; use std::collections::HashSet; @@ -481,7 +482,7 @@ mod tests { mod bench { use std::collections::HashMap; - use crate::prelude::triton_vm; + use crate::{models::consensus::mast_hash::MastHash, prelude::triton_vm}; use rand::{rngs::StdRng, Rng, SeedableRng}; use tasm_lib::{ diff --git a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs index 7c43e00b2..30bf71679 100644 --- a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs +++ b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs @@ -454,6 +454,8 @@ mod tests { use twenty_first::shared_math::tip5::Tip5State; use twenty_first::util_types::algebraic_hasher::Domain; + use crate::models::consensus::mast_hash::MastHash; + use super::*; #[test] diff --git a/src/models/blockchain/transaction/validity/typescripts_halt.rs b/src/models/blockchain/transaction/validity/typescripts_halt.rs index 777788c6a..cfc332bd4 100644 --- a/src/models/blockchain/transaction/validity/typescripts_halt.rs +++ b/src/models/blockchain/transaction/validity/typescripts_halt.rs @@ -1,4 +1,7 @@ -use crate::prelude::{triton_vm, twenty_first}; +use crate::{ + models::consensus::mast_hash::MastHash, + prelude::{triton_vm, twenty_first}, +}; use get_size::GetSize; use itertools::Itertools; diff --git a/src/models/consensus/mast_hash.rs b/src/models/consensus/mast_hash.rs new file mode 100644 index 000000000..0c003502b --- /dev/null +++ b/src/models/consensus/mast_hash.rs @@ -0,0 +1,48 @@ +use itertools::Itertools; +use tasm_lib::twenty_first::{ + shared_math::{b_field_element::BFieldElement, tip5::Digest}, + util_types::{ + algebraic_hasher::AlgebraicHasher, + merkle_tree::{CpuParallel, MerkleTree}, + merkle_tree_maker::MerkleTreeMaker, + }, +}; + +use crate::models::blockchain::shared::Hash; + +pub trait HasDiscriminant { + fn discriminant(&self) -> usize; +} + +pub trait MastHash { + type FieldEnum: HasDiscriminant; + + fn mast_sequences(&self) -> Vec>; + + fn merkle_tree(&self) -> MerkleTree { + let mut sequences = self.mast_sequences().to_vec(); + + // pad until length is a power of two + while sequences.len() & (sequences.len() - 1) != 0 { + sequences.push(vec![]); + } + + CpuParallel::from_digests( + &sequences + .into_iter() + .map(|seq| Hash::hash_varlen(&seq)) + .collect_vec(), + ) + .unwrap() + } + + fn mast_hash(&self) -> Digest { + self.merkle_tree().root() + } + + fn mast_path(&self, field: Self::FieldEnum) -> Vec { + self.merkle_tree() + .authentication_structure(&[field.discriminant()]) + .unwrap() + } +} diff --git a/src/models/consensus/mod.rs b/src/models/consensus/mod.rs index 48198e6b4..79686909f 100644 --- a/src/models/consensus/mod.rs +++ b/src/models/consensus/mod.rs @@ -13,6 +13,8 @@ use triton_vm::prelude::Proof; use triton_vm::prelude::PublicInput; use triton_vm::prelude::StarkParameters; +pub mod mast_hash; + /// This file contains abstractions for verifying consensus logic using TritonVM STARK /// proofs. The concrete logic is specified in the directories `transaction` and `block`. diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index cd634f4bf..4fd1162dc 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -434,7 +434,7 @@ mod wallet_tests { next_block .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .verify(Hash::hash(&genesis_block_output_utxo), &ms_membership_proof), "Membership proof must be valid after updating wallet state with generated blocks" ); @@ -509,7 +509,7 @@ mod wallet_tests { let membership_proof_is_valid = block_1 .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); assert!(membership_proof_is_valid); } @@ -527,7 +527,7 @@ mod wallet_tests { let membership_proof_is_valid = block_3 .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); assert!( !membership_proof_is_valid, diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index b5de243dd..840b13c03 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -207,9 +207,12 @@ impl WalletState { } } - ret.update_wallet_state_with_new_block(&Block::genesis_block()) - .await - .expect("Updating wallet state with genesis block must succeed"); + ret.update_wallet_state_with_new_block( + &MutatorSetAccumulator::default(), + &Block::genesis_block(), + ) + .await + .expect("Updating wallet state with genesis block must succeed"); } ret @@ -274,8 +277,12 @@ impl WalletState { /// Update wallet state with new block. Assumes the given block /// is valid and that the wallet state is not up to date yet. - pub async fn update_wallet_state_with_new_block(&mut self, block: &Block) -> Result<()> { - let transaction: Transaction = block.kernel.body.transaction.clone(); + pub async fn update_wallet_state_with_new_block( + &mut self, + current_mutator_set_accumulator: &MutatorSetAccumulator, + new_block: &Block, + ) -> Result<()> { + let transaction: Transaction = new_block.kernel.body.transaction.clone(); let spent_inputs: Vec<(Utxo, AbsoluteIndexSet, u64)> = self.scan_for_spent_utxos(&transaction); @@ -324,7 +331,7 @@ impl WalletState { let utxo_digest = Hash::hash(&monitored_utxo.utxo); match monitored_utxo - .get_membership_proof_for_block(block.kernel.header.prev_block_digest) + .get_membership_proof_for_block(new_block.kernel.header.prev_block_digest) { Some(ms_mp) => { debug!("Found valid mp for UTXO"); @@ -351,7 +358,7 @@ impl WalletState { None => String::from("No info about when UTXO was confirmed."), }; warn!( - "Unable to find valid membership proof for UTXO with digest {utxo_digest}. {confirmed_in_block_info} Current block height is {}", block.kernel.header.height + "Unable to find valid membership proof for UTXO with digest {utxo_digest}. {confirmed_in_block_info} Current block height is {}", new_block.kernel.header.height ); } } @@ -362,18 +369,14 @@ impl WalletState { // a) update all existing MS membership proofs // b) Register incoming transactions and derive their membership proofs let mut changed_mps = vec![]; - let mut msa_state: MutatorSetAccumulator = block - .kernel - .body - .previous_mutator_set_accumulator - .to_owned(); + let mut msa_state: MutatorSetAccumulator = current_mutator_set_accumulator.clone(); let mut removal_records = transaction.kernel.inputs.clone(); removal_records.reverse(); let mut removal_records: Vec<&mut RemovalRecord> = removal_records.iter_mut().collect::>(); - for addition_record in block + for addition_record in new_block .kernel .body .transaction @@ -420,8 +423,8 @@ impl WalletState { let receiver_preimage = addition_record_to_utxo_info[&addition_record].2; info!( "Received UTXO in block {}, height {}: value = {}", - block.hash().emojihash(), - block.kernel.header.height, + new_block.hash().emojihash(), + new_block.kernel.header.height, utxo.coins .iter() .filter(|coin| coin.type_script_hash == NATIVE_COIN_TYPESCRIPT_DIGEST) @@ -456,9 +459,9 @@ impl WalletState { // Add the new UTXO to the list of monitored UTXOs let mut mutxo = MonitoredUtxo::new(utxo, self.number_of_mps_per_utxo); mutxo.confirmed_in_block = Some(( - block.hash(), - Duration::from_millis(block.kernel.header.timestamp.value()), - block.kernel.header.height, + new_block.hash(), + Duration::from_millis(new_block.kernel.header.timestamp.value()), + new_block.kernel.header.height, )); monitored_utxos.push(mutxo); } @@ -471,7 +474,7 @@ impl WalletState { let mutxo_with_valid_mps = monitored_utxos .iter() .filter(|(_i, mutxo)| { - mutxo.is_synced_to(block.kernel.header.prev_block_digest) + mutxo.is_synced_to(new_block.kernel.header.prev_block_digest) || mutxo.blockhash_to_membership_proof.is_empty() }) .count(); @@ -485,7 +488,7 @@ impl WalletState { debug!("Block has {} removal records", removal_records.len()); debug!( "Transaction has {} inputs", - block.kernel.body.transaction.kernel.inputs.len() + new_block.kernel.body.transaction.kernel.inputs.len() ); let mut block_tx_input_count = 0; while let Some(removal_record) = removal_records.pop() { @@ -522,9 +525,9 @@ impl WalletState { let mut spent_mutxo = monitored_utxos.get(*mutxo_list_index); spent_mutxo.spent_in_block = Some(( - block.hash(), - Duration::from_millis(block.kernel.header.timestamp.value()), - block.kernel.header.height, + new_block.hash(), + Duration::from_millis(new_block.kernel.header.timestamp.value()), + new_block.kernel.header.height, )); monitored_utxos.set(*mutxo_list_index, spent_mutxo); } @@ -536,12 +539,7 @@ impl WalletState { // Sanity check that `msa_state` agrees with the mutator set from the applied block assert_eq!( - block - .kernel - .body - .next_mutator_set_accumulator - .clone() - .hash(), + new_block.kernel.body.mutator_set_accumulator.clone().hash(), msa_state.hash(), "Mutator set in wallet-handler must agree with that from applied block" ); @@ -562,7 +560,7 @@ impl WalletState { { let StrongUtxoKey { utxo_digest, .. } = strong_utxo_key; let mut monitored_utxo = monitored_utxos.get(*own_utxo_index); - monitored_utxo.add_membership_proof_for_tip(block.hash(), updated_ms_mp.to_owned()); + monitored_utxo.add_membership_proof_for_tip(new_block.hash(), updated_ms_mp.to_owned()); // Sanity check that membership proofs of non-spent transactions are still valid assert!( @@ -583,7 +581,7 @@ impl WalletState { self.store_utxo_ms_recovery_data(item)?; } - self.wallet_db.set_sync_label(block.hash()); + self.wallet_db.set_sync_label(new_block.hash()); self.wallet_db.persist(); // Mark all expected UTXOs that were received in this block as received @@ -591,7 +589,7 @@ impl WalletState { .into_iter() .for_each(|(addition_rec, _, _, _)| { self.expected_utxos - .mark_as_received(addition_rec, block.hash()) + .mark_as_received(addition_rec, new_block.hash()) .expect("Expected UTXO must be present when marking it as received") }); @@ -761,6 +759,7 @@ mod tests { .wallet_db .monitored_utxos() .len(); + let mut mutator_set_accumulator = MutatorSetAccumulator::::default(); assert!( monitored_utxos_count_init.is_zero(), "Monitored UTXO list must be empty at init" @@ -780,7 +779,7 @@ mod tests { make_mock_block(&latest_block, None, other_recipient_address); own_global_state .wallet_state - .update_wallet_state_with_new_block(&new_block) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &new_block) .await .unwrap(); own_global_state @@ -798,6 +797,7 @@ mod tests { .set_block(new_block.clone()); latest_block = new_block; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; } assert!( own_global_state @@ -829,7 +829,7 @@ mod tests { .unwrap(); own_global_state .wallet_state - .update_wallet_state_with_new_block(&block_3a) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_3a) .await .unwrap(); own_global_state @@ -876,7 +876,7 @@ mod tests { make_mock_block(&latest_block, None, other_recipient_address); own_global_state .wallet_state - .update_wallet_state_with_new_block(&block_3b) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_3b) .await .unwrap(); own_global_state @@ -916,12 +916,13 @@ mod tests { // Mine nine blocks on top of 3b, update states latest_block = block_3b; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; for _ in 4..=11 { let (new_block, _new_block_coinbase_utxo, _new_block_coinbase_sender_randomness) = make_mock_block(&latest_block, None, other_recipient_address); own_global_state .wallet_state - .update_wallet_state_with_new_block(&new_block) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &new_block) .await .unwrap(); own_global_state @@ -939,6 +940,7 @@ mod tests { .set_block(new_block.clone()); latest_block = new_block; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; } let prune_count_11 = own_global_state @@ -965,7 +967,7 @@ mod tests { let (block_12, _, _) = make_mock_block(&latest_block, None, other_recipient_address); own_global_state .wallet_state - .update_wallet_state_with_new_block(&block_12) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_12) .await .unwrap(); own_global_state diff --git a/src/peer_loop.rs b/src/peer_loop.rs index 1f87ef816..de7c6b6c2 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -513,7 +513,7 @@ impl PeerLoopHandler { if global_state .chain .archival_state() - .block_belongs_to_canonical_chain(&child, &tip_header) + .block_belongs_to_canonical_chain(&child, tip_header) .await { canonical = child; diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 60d383b98..282ac848c 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -950,9 +950,7 @@ pub fn make_mock_block( let block_body: BlockBody = BlockBody { transaction, - next_mutator_set_accumulator: next_mutator_set.clone(), - - previous_mutator_set_accumulator: previous_mutator_set, + mutator_set_accumulator: next_mutator_set.clone(), }; let block_target_difficulty = previous_block.kernel.header.difficulty; From 4a694c4c3442b666228ce9cb403b07e7d0499827 Mon Sep 17 00:00:00 2001 From: Alan Date: Fri, 26 Jan 2024 22:53:20 +0100 Subject: [PATCH 08/29] refactor: make compile --- src/mine_loop.rs | 25 ++---- src/models/blockchain/block/block_body.rs | 10 +-- src/models/blockchain/block/block_kernel.rs | 4 +- src/models/blockchain/block/mod.rs | 5 +- src/models/state/archival_state.rs | 49 +++++++++--- src/models/state/mempool.rs | 42 +++++++--- src/models/state/mod.rs | 88 ++++++++++++++------- src/models/state/wallet/mod.rs | 88 ++++++++++++++------- src/models/state/wallet/wallet_state.rs | 8 +- src/peer_loop.rs | 2 +- src/tests/shared.rs | 9 +-- 11 files changed, 215 insertions(+), 115 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 5b9af6b99..6f3f41a04 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -27,6 +27,7 @@ use rand::SeedableRng; use std::ops::Deref; use std::time::Duration; use std::time::{SystemTime, UNIX_EPOCH}; +use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use tokio::select; use tokio::sync::{mpsc, watch}; use tokio::task::JoinHandle; @@ -47,11 +48,8 @@ fn make_block_template( ) -> (BlockHeader, BlockBody) { let additions = transaction.kernel.outputs.clone(); let removals = transaction.kernel.inputs.clone(); - let mut next_mutator_set_accumulator: MutatorSetAccumulator = previous_block - .kernel - .body - .next_mutator_set_accumulator - .clone(); + let mut next_mutator_set_accumulator: MutatorSetAccumulator = + previous_block.kernel.body.mutator_set_accumulator.clone(); // Apply the mutator set update to the mutator set accumulator // This function mutates the MS accumulator that is given as argument to @@ -63,12 +61,9 @@ fn make_block_template( let block_body: BlockBody = BlockBody { transaction, - next_mutator_set_accumulator: next_mutator_set_accumulator.clone(), - previous_mutator_set_accumulator: previous_block - .kernel - .body - .next_mutator_set_accumulator - .clone(), + mutator_set_accumulator: next_mutator_set_accumulator.clone(), + lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), + block_mmr_accumulator: MmrAccumulator::::new(vec![]), }; let zero = BFieldElement::zero(); @@ -275,11 +270,7 @@ fn create_block_transaction( receiving_address.privacy_digest, &global_state.wallet_state.wallet_secret, next_block_height, - latest_block - .kernel - .body - .next_mutator_set_accumulator - .clone(), + latest_block.kernel.body.mutator_set_accumulator.clone(), ); debug!( @@ -287,7 +278,7 @@ fn create_block_transaction( latest_block .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash() ); diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index 7a25a0943..c1f8fff33 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -11,7 +11,7 @@ use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::Transaction; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; -enum BlockBodyField { +pub enum BlockBodyField { Transaction, MutatorSetAccumulator, LockFreeMmrAccumulator, @@ -21,10 +21,10 @@ enum BlockBodyField { impl HasDiscriminant for BlockBodyField { fn discriminant(&self) -> usize { match self { - Transaction => 0, - MutatorSetAccumulator => 1, - LockFreeMmrAccumulator => 2, - BlockMmrAccumulator => 3, + BlockBodyField::Transaction => 0, + BlockBodyField::MutatorSetAccumulator => 1, + BlockBodyField::LockFreeMmrAccumulator => 2, + BlockBodyField::BlockMmrAccumulator => 3, } } } diff --git a/src/models/blockchain/block/block_kernel.rs b/src/models/blockchain/block/block_kernel.rs index b0e17500e..57ce60a57 100644 --- a/src/models/blockchain/block/block_kernel.rs +++ b/src/models/blockchain/block/block_kernel.rs @@ -21,8 +21,8 @@ pub enum BlockKernelField { impl HasDiscriminant for BlockKernelField { fn discriminant(&self) -> usize { match self { - Header => 0, - Body => 1, + BlockKernelField::Header => 0, + BlockKernelField::Body => 1, } } } diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index d9cfc12be..686687d5c 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -119,7 +119,6 @@ impl Block { } pub fn genesis_block() -> Self { - let empty_mutator_set = MutatorSetAccumulator::::default(); let mut genesis_mutator_set = MutatorSetAccumulator::::default(); let mut ms_update = MutatorSetUpdate::default(); @@ -236,8 +235,8 @@ impl Block { let block_body: BlockBody = BlockBody { transaction: new_transaction, mutator_set_accumulator: next_mutator_set_accumulator.clone(), - lock_free_mmr_accumulator: self.kernel.body.lock_free_mmr_accumulator, - block_mmr_accumulator: self.kernel.body.block_mmr_accumulator, + lock_free_mmr_accumulator: self.kernel.body.lock_free_mmr_accumulator.clone(), + block_mmr_accumulator: self.kernel.body.block_mmr_accumulator.clone(), }; let block_header = BlockHeader { diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 16d25bf6b..820bbcab0 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -705,7 +705,7 @@ impl ArchivalState { assert_eq!( new_block .kernel.body - .next_mutator_set_accumulator + .mutator_set_accumulator .hash(), self.archival_mutator_set.ams().hash(), "Calculated archival mutator set commitment must match that from newly added block. Block Digest: {:?}", new_block.hash() @@ -875,9 +875,17 @@ mod archival_state_tests { .update_mutator_set(&mock_block_1) .await .unwrap(); + let msa = genesis_receiver_global_state + .chain + .archival_state_mut() + .genesis_block + .kernel + .body + .mutator_set_accumulator + .clone(); genesis_receiver_global_state .wallet_state - .update_wallet_state_with_new_block(&mock_block_1) + .update_wallet_state_with_new_block(&msa, &mock_block_1) .await .unwrap(); @@ -1177,7 +1185,10 @@ mod archival_state_tests { // 3. Update wallet state so we can continue making transactions global_state .wallet_state - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block( + &previous_block.kernel.body.mutator_set_accumulator, + &next_block, + ) .await .unwrap(); } @@ -1407,7 +1418,10 @@ mod archival_state_tests { .unwrap(); genesis_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await .unwrap(); assert_eq!( @@ -1436,7 +1450,10 @@ mod archival_state_tests { } alice_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await .unwrap(); } @@ -1457,7 +1474,10 @@ mod archival_state_tests { } bob_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await .unwrap(); } @@ -1577,14 +1597,20 @@ mod archival_state_tests { .lock_guard_mut() .await .wallet_state - .update_wallet_state_with_new_block(&block_2) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2, + ) .await .unwrap(); bob_state_lock .lock_guard_mut() .await .wallet_state - .update_wallet_state_with_new_block(&block_2) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2, + ) .await .unwrap(); assert!(alice_state_lock @@ -1647,7 +1673,10 @@ mod archival_state_tests { .lock_guard_mut() .await .wallet_state - .update_wallet_state_with_new_block(&block_2) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2, + ) .await .unwrap(); @@ -1666,7 +1695,7 @@ mod archival_state_tests { let state = state_lock.lock_guard().await; assert_eq!( - block_2.kernel.body.next_mutator_set_accumulator, + block_2.kernel.body.mutator_set_accumulator, state .chain .archival_state() diff --git a/src/models/state/mempool.rs b/src/models/state/mempool.rs index 4a0a27999..467592cab 100644 --- a/src/models/state/mempool.rs +++ b/src/models/state/mempool.rs @@ -8,7 +8,9 @@ //! are interested in the transaction with either the highest or the lowest 'fee //! density'. -use crate::prelude::twenty_first; +use crate::{ + prelude::twenty_first, util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator, +}; use bytesize::ByteSize; use get_size::GetSize; @@ -62,8 +64,10 @@ fn now() -> Duration { #[derive(Debug, Clone, PartialEq, Eq, GetSize)] pub struct Mempool { max_total_size: usize, + // Maintain for constant lookup tx_dictionary: HashMap, + // Maintain for fast min and max #[get_size(ignore)] // This is relatively small compared to `LookupTable` queue: DoublePriorityQueue, @@ -288,7 +292,11 @@ impl Mempool { /// This function remove from the mempool all those transactions that become invalid because /// of this newly mined block. It also updates all mutator set data for the monitored /// transactions that were not removed due to being included in the block. - pub fn update_with_block(&mut self, block: &Block) { + pub fn update_with_block( + &mut self, + previous_mutator_set_accumulator: MutatorSetAccumulator, + block: &Block, + ) { // Check if the sets of inserted indices in the block transaction // and transactions in the mempool are disjoint. // Removes the transaction from the mempool if they are not as this would @@ -323,7 +331,7 @@ impl Mempool { // Update the remaining transactions so their mutator set data is still valid for tx in self.tx_dictionary.values_mut() { - tx.update_mutator_set_records(block) + tx.update_mutator_set_records(&previous_mutator_set_accumulator, block) .expect("Updating mempool transaction must succeed"); } @@ -548,7 +556,10 @@ mod tests { // Update both states with block 1 premine_receiver_global_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await?; premine_receiver_global_state .chain @@ -566,7 +577,10 @@ mod tests { .expect("UTXO notification from miner must be accepted"); other_global_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await?; other_global_state .chain @@ -624,7 +638,7 @@ mod tests { // Update the mempool with block 2 and verify that the mempool now only contains one tx assert_eq!(2, mempool.len()); - mempool.update_with_block(&block_2); + mempool.update_with_block(block_1.kernel.body.mutator_set_accumulator, &block_2); assert_eq!(1, mempool.len()); // Create a new block to verify that the non-mined transaction still contains @@ -638,10 +652,10 @@ mod tests { debug!( "Just made block with previous mutator set hash {}", - block_3_with_updated_tx + block_2 .kernel .body - .previous_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash() ); @@ -650,7 +664,7 @@ mod tests { block_3_with_updated_tx .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash() ); @@ -671,7 +685,10 @@ mod tests { let mut previous_block = block_3_with_no_input; for _ in 0..10 { let (next_block, _, _) = make_mock_block(&previous_block, None, other_receiver_address); - mempool.update_with_block(&next_block); + mempool.update_with_block( + previous_block.kernel.body.mutator_set_accumulator, + &next_block, + ); previous_block = next_block; } @@ -684,7 +701,10 @@ mod tests { "Block with tx with updated mutator set data must be valid after 10 blocks have been mined" ); - mempool.update_with_block(&block_14); + mempool.update_with_block( + previous_block.kernel.body.mutator_set_accumulator, + &block_14, + ); assert!( mempool.is_empty(), diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 216b7780d..af1280e6c 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -1,4 +1,5 @@ use crate::prelude::twenty_first; +use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; use anyhow::{bail, Result}; use itertools::Itertools; @@ -397,7 +398,7 @@ impl GlobalState { .await?; // Create all removal records. These must be relative to the block tip. - let msa_tip = &bc_tip.kernel.body.next_mutator_set_accumulator; + let msa_tip = &bc_tip.kernel.body.mutator_set_accumulator; let mut inputs: Vec> = vec![]; let mut input_amount: Amount = Amount::zero(); for (spendable_utxo, _lock_script, mp) in spendable_utxos_and_mps.iter() { @@ -516,13 +517,13 @@ impl GlobalState { // sanity check: test membership proofs for (utxo, membership_proof) in input_utxos.iter().zip(input_membership_proofs.iter()) { let item = Hash::hash(utxo); - assert!(self.chain.light_state().body().next_mutator_set_accumulator.verify(item, membership_proof), "sanity check failed: trying to generate transaction with invalid membership proofs for inputs!"); + assert!(self.chain.light_state().body().mutator_set_accumulator.verify(item, membership_proof), "sanity check failed: trying to generate transaction with invalid membership proofs for inputs!"); debug!( "Have valid membership proofs relative to {}", self.chain .light_state() .body() - .next_mutator_set_accumulator + .mutator_set_accumulator .hash() .emojihash() ); @@ -532,7 +533,7 @@ impl GlobalState { .chain .light_state() .body() - .next_mutator_set_accumulator + .mutator_set_accumulator .clone(); // When reading a digest from secret and standard-in, the digest's @@ -773,6 +774,15 @@ impl GlobalState { .get_block(revert_block_hash) .await? .unwrap(); + let maybe_revert_block_predecessor = self + .chain + .archival_state() + .get_block(revert_block.kernel.header.prev_block_digest) + .await?; + let previous_mutator_set = match maybe_revert_block_predecessor { + Some(block) => block.kernel.body.mutator_set_accumulator, + None => MutatorSetAccumulator::::default(), + }; debug!("MUTXO confirmed at height {confirming_block_height}, reverting for height {} on abandoned chain", revert_block.kernel.header.height); @@ -786,11 +796,6 @@ impl GlobalState { } // revert additions - let previous_mutator_set = revert_block - .kernel - .body - .previous_mutator_set_accumulator - .clone(); membership_proof.revert_update_from_batch_addition(&previous_mutator_set); // unset spent_in_block field if the UTXO was spent in this block @@ -821,13 +826,17 @@ impl GlobalState { .get_block(apply_block_hash) .await? .unwrap(); + let maybe_apply_block_predecessor = self + .chain + .archival_state() + .get_block(apply_block.kernel.header.prev_block_digest) + .await?; + let mut block_msa = match maybe_apply_block_predecessor { + Some(block) => block.kernel.body.mutator_set_accumulator, + None => MutatorSetAccumulator::::default(), + }; let addition_records = apply_block.kernel.body.transaction.kernel.outputs; let removal_records = apply_block.kernel.body.transaction.kernel.inputs; - let mut block_msa = apply_block - .kernel - .body - .previous_mutator_set_accumulator - .clone(); // apply additions for addition_record in addition_records.iter() { @@ -849,10 +858,7 @@ impl GlobalState { block_msa.remove(removal_record); } - assert_eq!( - block_msa, - apply_block.kernel.body.next_mutator_set_accumulator - ); + assert_eq!(block_msa, apply_block.kernel.body.mutator_set_accumulator); } // store updated membership proof @@ -996,6 +1002,13 @@ impl GlobalState { ) -> Result<()> { // get proof_of_work_family for tip let tip_proof_of_work_family = self.chain.light_state().kernel.header.proof_of_work_family; + let previous_mutator_set_accumulator = self + .chain + .light_state() + .kernel + .body + .mutator_set_accumulator + .clone(); // Apply the updates self.chain @@ -1025,12 +1038,13 @@ impl GlobalState { // update wallet state with relevant UTXOs from this block self.wallet_state - .update_wallet_state_with_new_block(&new_block) + .update_wallet_state_with_new_block(&previous_mutator_set_accumulator, &new_block) .await?; // Update mempool with UTXOs from this block. This is done by removing all transaction // that became invalid/was mined by this block. - self.mempool.update_with_block(&new_block); + self.mempool + .update_with_block(previous_mutator_set_accumulator, &new_block); self.chain.light_state_mut().set_block(new_block); @@ -1095,7 +1109,7 @@ mod global_state_tests { if !tip_block .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .verify(Hash::hash(&monitored_utxo.utxo), &mp) { return false; @@ -1252,7 +1266,7 @@ mod global_state_tests { .chain .light_state() .body() - .next_mutator_set_accumulator + .mutator_set_accumulator .verify( ms_item, &own_premine_mutxo @@ -1374,7 +1388,10 @@ mod global_state_tests { .unwrap(); global_state .wallet_state - .update_wallet_state_with_new_block(&mock_block_1a) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &mock_block_1a, + ) .await .unwrap(); } @@ -1403,7 +1420,10 @@ mod global_state_tests { .await?; global_state .wallet_state - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block( + &parent_block.kernel.body.mutator_set_accumulator, + &next_block, + ) .await .unwrap(); parent_block = next_block; @@ -1487,7 +1507,10 @@ mod global_state_tests { .unwrap(); global_state .wallet_state - .update_wallet_state_with_new_block(&mock_block_1a) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &mock_block_1a, + ) .await .unwrap(); @@ -1513,7 +1536,10 @@ mod global_state_tests { .await?; global_state .wallet_state - .update_wallet_state_with_new_block(&next_a_block) + .update_wallet_state_with_new_block( + &fork_a_block.kernel.body.mutator_set_accumulator, + &next_a_block, + ) .await .unwrap(); fork_a_block = next_a_block; @@ -1541,7 +1567,10 @@ mod global_state_tests { .await?; global_state .wallet_state - .update_wallet_state_with_new_block(&next_b_block) + .update_wallet_state_with_new_block( + &fork_b_block.kernel.body.mutator_set_accumulator, + &next_b_block, + ) .await .unwrap(); fork_b_block = next_b_block; @@ -1591,7 +1620,10 @@ mod global_state_tests { .await?; global_state .wallet_state - .update_wallet_state_with_new_block(&next_c_block) + .update_wallet_state_with_new_block( + &fork_c_block.kernel.body.mutator_set_accumulator, + &next_c_block, + ) .await .unwrap(); fork_c_block = next_c_block; diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index 4fd1162dc..5d18b9cec 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -360,6 +360,7 @@ mod wallet_tests { add_block, get_mock_global_state, get_mock_wallet_state, make_mock_block, make_mock_transaction_with_generation_key, }; + use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; use crate::util_types::mutator_set::mutator_set_trait::MutatorSet; async fn get_monitored_utxos(wallet_state: &WalletState) -> Vec { @@ -414,8 +415,9 @@ mod wallet_tests { let (nb, _coinbase_utxo, _sender_randomness) = make_mock_block(&previous_block, None, other_receiver_address); next_block = nb; + let mutator_set_accumulator = next_block.kernel.body.mutator_set_accumulator.clone(); wallet_state_premine_recipient - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &next_block) .await?; } @@ -462,6 +464,7 @@ mod wallet_tests { let genesis_block = Block::genesis_block(); let own_spending_key = own_wallet_secret.nth_generation_spending_key(0); let own_recipient_address = own_spending_key.to_address(); + let mutator_set_accumulator = MutatorSetAccumulator::::default(); let (block_1, block_1_coinbase_utxo, block_1_coinbase_sender_randomness) = make_mock_block(&genesis_block, None, own_recipient_address); @@ -480,7 +483,7 @@ mod wallet_tests { "Expected UTXO list must have length 1 before block registration" ); own_wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_1) .await?; assert_eq!( 1, @@ -536,10 +539,16 @@ mod wallet_tests { } // Verify that the membership proof is valid *after* running the updater own_wallet_state - .update_wallet_state_with_new_block(&block_2) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2, + ) .await?; own_wallet_state - .update_wallet_state_with_new_block(&block_3) + .update_wallet_state_with_new_block( + &block_2.kernel.body.mutator_set_accumulator, + &block_3, + ) .await?; monitored_utxos = get_monitored_utxos(&own_wallet_state).await; @@ -551,7 +560,7 @@ mod wallet_tests { let membership_proof_is_valid = block_3 .kernel .body - .next_mutator_set_accumulator + .mutator_set_accumulator .verify(block_1_tx_output_digest, &ms_membership_proof); assert!( membership_proof_is_valid, @@ -587,7 +596,7 @@ mod wallet_tests { ) .unwrap(); own_wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block(&MutatorSetAccumulator::default(), &block_1) .await?; // Verify that the allocater returns a sane amount @@ -641,7 +650,10 @@ mod wallet_tests { ) .unwrap(); own_wallet_state - .update_wallet_state_with_new_block(&next_block_prime) + .update_wallet_state_with_new_block( + &previous_block.kernel.body.mutator_set_accumulator, + &next_block_prime, + ) .await?; next_block = next_block_prime; } @@ -703,17 +715,13 @@ mod wallet_tests { Into::::into(22u64), next_block.kernel.header.height ); + let msa_tip_previous = next_block.kernel.body.mutator_set_accumulator.clone(); (next_block, _, _) = make_mock_block(&next_block.clone(), None, own_spending_key.to_address()); assert_eq!( Into::::into(23u64), next_block.kernel.header.height ); - let msa_tip_previous = next_block - .kernel - .body - .previous_mutator_set_accumulator - .clone(); let receiver_data = vec![UtxoReceiverData { utxo: Utxo { @@ -732,12 +740,12 @@ mod wallet_tests { input_utxos_mps_keys, receiver_data, Amount::zero(), - msa_tip_previous, + msa_tip_previous.clone(), ); next_block.accumulate_transaction(tx); own_wallet_state - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block(&msa_tip_previous.clone(), &next_block) .await?; assert_eq!( @@ -786,6 +794,7 @@ mod wallet_tests { "Premine must have non-zero synced balance" ); + let previous_msa = MutatorSetAccumulator::default(); let (mut block_1, _, _) = make_mock_block(&genesis_block, None, own_address); let receiver_data_12_to_other = UtxoReceiverData { @@ -849,14 +858,14 @@ mod wallet_tests { .unwrap(); } own_wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block(&previous_msa, &block_1) .await?; add_block(&mut premine_receiver_global_state, block_1.clone()) .await .unwrap(); premine_receiver_global_state .wallet_state - .update_wallet_state_with_new_block(&block_1) + .update_wallet_state_with_new_block(&previous_msa, &block_1) .await?; assert_eq!( preminers_original_balance @@ -881,7 +890,7 @@ mod wallet_tests { // Verify that all monitored UTXOs have valid membership proofs for monitored_utxo in monitored_utxos { assert!( - block_1.kernel.body.next_mutator_set_accumulator.verify( + block_1.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_1.hash()) @@ -908,14 +917,20 @@ mod wallet_tests { ) .unwrap(); own_wallet_state - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block( + &previous_block.kernel.body.mutator_set_accumulator, + &next_block, + ) .await?; add_block(&mut premine_receiver_global_state, block_1.clone()) .await .unwrap(); premine_receiver_global_state .wallet_state - .update_wallet_state_with_new_block(&next_block) + .update_wallet_state_with_new_block( + &previous_block.kernel.body.mutator_set_accumulator, + &next_block, + ) .await?; } @@ -928,7 +943,7 @@ mod wallet_tests { ); for monitored_utxo in monitored_utxos { assert!( - block_18.kernel.body.next_mutator_set_accumulator.verify( + block_18.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_18.hash()) @@ -973,14 +988,20 @@ mod wallet_tests { let (block_2_b, _, _) = make_mock_block(&block_1, None, premine_wallet_spending_key.to_address()); own_wallet_state - .update_wallet_state_with_new_block(&block_2_b) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2_b, + ) .await?; add_block(&mut premine_receiver_global_state, block_2_b.clone()) .await .unwrap(); premine_receiver_global_state .wallet_state - .update_wallet_state_with_new_block(&block_2_b) + .update_wallet_state_with_new_block( + &block_1.kernel.body.mutator_set_accumulator, + &block_2_b, + ) .await .unwrap(); let monitored_utxos_at_2b: Vec<_> = get_monitored_utxos(&own_wallet_state) @@ -997,7 +1018,7 @@ mod wallet_tests { // Verify that all monitored UTXOs (with synced MPs) have valid membership proofs for monitored_utxo in monitored_utxos_at_2b.iter() { assert!( - block_2_b.kernel.body.next_mutator_set_accumulator.verify( + block_2_b.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_2_b.hash()) @@ -1012,7 +1033,10 @@ mod wallet_tests { let (block_19, _, _) = make_mock_block(&block_18, None, premine_wallet_spending_key.to_address()); own_wallet_state - .update_wallet_state_with_new_block(&block_19) + .update_wallet_state_with_new_block( + &block_18.kernel.body.mutator_set_accumulator, + &block_19, + ) .await?; let monitored_utxos_block_19: Vec<_> = get_monitored_utxos(&own_wallet_state) .await @@ -1028,7 +1052,7 @@ mod wallet_tests { // Verify that all monitored UTXOs have valid membership proofs for monitored_utxo in monitored_utxos_block_19.iter() { assert!( - block_19.kernel.body.next_mutator_set_accumulator.verify( + block_19.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_19.hash()) @@ -1084,7 +1108,10 @@ mod wallet_tests { ) .unwrap(); own_wallet_state - .update_wallet_state_with_new_block(&block_3_b) + .update_wallet_state_with_new_block( + &block_2_b.kernel.body.mutator_set_accumulator, + &block_3_b, + ) .await?; let monitored_utxos_3b: Vec<_> = get_monitored_utxos(&own_wallet_state) @@ -1110,7 +1137,7 @@ mod wallet_tests { for monitored_utxo in monitored_utxos_3b { assert!( monitored_utxo.spent_in_block.is_some() - || block_3_b.kernel.body.next_mutator_set_accumulator.verify( + || block_3_b.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_3_b.hash()) @@ -1124,7 +1151,10 @@ mod wallet_tests { let (block_20, _, _) = make_mock_block(&block_19, None, premine_wallet_spending_key.to_address()); own_wallet_state - .update_wallet_state_with_new_block(&block_20) + .update_wallet_state_with_new_block( + &block_19.kernel.body.mutator_set_accumulator, + &block_20, + ) .await?; // Verify that we have two membership proofs of `forked_utxo`: one matching block20 and one matching block_3b @@ -1141,7 +1171,7 @@ mod wallet_tests { for monitored_utxo in monitored_utxos_20.iter() { assert!( monitored_utxo.spent_in_block.is_some() - || block_20.kernel.body.next_mutator_set_accumulator.verify( + || block_20.kernel.body.mutator_set_accumulator.verify( Hash::hash(&monitored_utxo.utxo), &monitored_utxo .get_membership_proof_for_block(block_20.hash()) diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index 840b13c03..14b6585cb 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -797,7 +797,7 @@ mod tests { .set_block(new_block.clone()); latest_block = new_block; - mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator.clone(); } assert!( own_global_state @@ -816,7 +816,7 @@ mod tests { // Add block 3a with a coinbase UTXO for us let own_recipient_address = own_spending_key.to_address(); let (block_3a, block_3a_coinbase_utxo, block_3a_coinbase_sender_randomness) = - make_mock_block(&latest_block, None, own_recipient_address); + make_mock_block(&latest_block.clone(), None, own_recipient_address); own_global_state .wallet_state .expected_utxos @@ -916,7 +916,7 @@ mod tests { // Mine nine blocks on top of 3b, update states latest_block = block_3b; - mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator.clone(); for _ in 4..=11 { let (new_block, _new_block_coinbase_utxo, _new_block_coinbase_sender_randomness) = make_mock_block(&latest_block, None, other_recipient_address); @@ -940,7 +940,7 @@ mod tests { .set_block(new_block.clone()); latest_block = new_block; - mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator; + mutator_set_accumulator = latest_block.kernel.body.mutator_set_accumulator.clone(); } let prune_count_11 = own_global_state diff --git a/src/peer_loop.rs b/src/peer_loop.rs index de7c6b6c2..8ec85d65e 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -777,7 +777,7 @@ impl PeerLoopHandler { .light_state() .kernel .body - .next_mutator_set_accumulator, + .mutator_set_accumulator, ); if !confirmable { warn!("Received unconfirmable tx"); diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 282ac848c..66202eb3b 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -28,6 +28,7 @@ use std::{ sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; +use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use tokio::sync::{broadcast, mpsc}; use tokio_serde::{formats::SymmetricalBincode, Serializer}; use tokio_util::codec::{Encoder, LengthDelimitedCodec}; @@ -903,11 +904,7 @@ pub fn make_mock_block( let coinbase_output_randomness: Digest = Digest::new(random_elements_array()); let receiver_digest: Digest = coinbase_beneficiary.privacy_digest; - let mut next_mutator_set = previous_block - .kernel - .body - .next_mutator_set_accumulator - .clone(); + let mut next_mutator_set = previous_block.kernel.body.mutator_set_accumulator.clone(); let previous_mutator_set = next_mutator_set.clone(); let coinbase_digest: Digest = Hash::hash(&coinbase_utxo); @@ -951,6 +948,8 @@ pub fn make_mock_block( let block_body: BlockBody = BlockBody { transaction, mutator_set_accumulator: next_mutator_set.clone(), + lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), + block_mmr_accumulator: MmrAccumulator::::new(vec![]), }; let block_target_difficulty = previous_block.kernel.header.difficulty; From 7d99673f81af3e199314ae687fd8dcb266f15e90 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Sat, 27 Jan 2024 21:44:28 +0100 Subject: [PATCH 09/29] fix: synchronize mast_hash trait with tasm snippets --- src/mine_loop.rs | 3 ++- src/models/blockchain/block/mod.rs | 8 ++++---- .../tasm/transaction_kernel_mast_hash.rs | 6 ------ src/models/consensus/mast_hash.rs | 18 ++++++++---------- src/models/state/wallet/mod.rs | 14 +++++++++----- src/models/state/wallet/wallet_state.rs | 2 +- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 6f3f41a04..1c9215dd9 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -10,6 +10,7 @@ use crate::models::blockchain::transaction::utxo::*; use crate::models::blockchain::transaction::validity::TransactionValidationLogic; use crate::models::blockchain::transaction::*; use crate::models::channel::*; +use crate::models::consensus::mast_hash::MastHash; use crate::models::shared::SIZE_20MB_IN_BYTES; use crate::models::state::wallet::utxo_notification_pool::{ExpectedUtxo, UtxoNotifier}; use crate::models::state::wallet::WalletSecret; @@ -85,7 +86,7 @@ fn make_block_template( version: zero, height: next_block_height, mutator_set_hash: mutator_set_commitment, - prev_block_digest: Hash::hash(&previous_block.kernel.header), + prev_block_digest: previous_block.kernel.mast_hash(), timestamp: BFieldElement::new(block_timestamp), nonce: [zero, zero, zero], max_block_size: MOCK_MAX_BLOCK_SIZE, diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 686687d5c..cf39ce666 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -275,10 +275,10 @@ impl Block { // d) Target difficulty, and other control parameters, were adjusted correctly // 1. The transaction is valid. // 1'. All transactions are valid. - // a) verify that MS membership proof is valid, done against `previous_mutator_set_accumulator`, - // b) Verify that MS removal record is valid, done against `previous_mutator_set_accumulator`, + // a) verify that MS membership proof is valid, done against previous `mutator_set_accumulator`, + // b) Verify that MS removal record is valid, done against previous `mutator_set_accumulator`, // c) Verify that all removal records have unique index sets - // d) verify that adding `mutator_set_update` to `previous_mutator_set_accumulator` + // d) verify that adding `mutator_set_update` to previous `mutator_set_accumulator` // gives `next_mutator_set_accumulator`, // e) transaction timestamp <= block timestamp // f) transaction coinbase <= miner reward @@ -291,7 +291,7 @@ impl Block { } // 0.b) Block header points to previous block - if previous_block.hash() != block_copy.kernel.header.prev_block_digest { + if previous_block.kernel.mast_hash() != block_copy.kernel.header.prev_block_digest { warn!("Hash digest does not match previous digest"); return false; } diff --git a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs index 30bf71679..5c97cc782 100644 --- a/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs +++ b/src/models/blockchain/transaction/validity/tasm/transaction_kernel_mast_hash.rs @@ -50,12 +50,6 @@ impl TransactionKernelMastHash { BFieldElement::new(transaction_kernel_encoded.len() as u64), ); - // set dynamic allocator - memory.insert( - ::zero(), - BFieldElement::new(transaction_kernel_encoded.len() as u64) + address, - ); - let mut stack = tasm_lib::empty_stack(); stack.push(address); ExecutionState { diff --git a/src/models/consensus/mast_hash.rs b/src/models/consensus/mast_hash.rs index 0c003502b..ba1c29cb8 100644 --- a/src/models/consensus/mast_hash.rs +++ b/src/models/consensus/mast_hash.rs @@ -20,20 +20,18 @@ pub trait MastHash { fn mast_sequences(&self) -> Vec>; fn merkle_tree(&self) -> MerkleTree { - let mut sequences = self.mast_sequences().to_vec(); + let mut digests = self + .mast_sequences() + .into_iter() + .map(|seq| Hash::hash_varlen(&seq)) + .collect_vec(); // pad until length is a power of two - while sequences.len() & (sequences.len() - 1) != 0 { - sequences.push(vec![]); + while digests.len() & (digests.len() - 1) != 0 { + digests.push(Digest::default()); } - CpuParallel::from_digests( - &sequences - .into_iter() - .map(|seq| Hash::hash_varlen(&seq)) - .collect_vec(), - ) - .unwrap() + CpuParallel::from_digests(&digests).unwrap() } fn mast_hash(&self) -> Digest { diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index 5d18b9cec..636de4f89 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -360,7 +360,6 @@ mod wallet_tests { add_block, get_mock_global_state, get_mock_wallet_state, make_mock_block, make_mock_transaction_with_generation_key, }; - use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; use crate::util_types::mutator_set::mutator_set_trait::MutatorSet; async fn get_monitored_utxos(wallet_state: &WalletState) -> Vec { @@ -464,7 +463,6 @@ mod wallet_tests { let genesis_block = Block::genesis_block(); let own_spending_key = own_wallet_secret.nth_generation_spending_key(0); let own_recipient_address = own_spending_key.to_address(); - let mutator_set_accumulator = MutatorSetAccumulator::::default(); let (block_1, block_1_coinbase_utxo, block_1_coinbase_sender_randomness) = make_mock_block(&genesis_block, None, own_recipient_address); @@ -483,7 +481,10 @@ mod wallet_tests { "Expected UTXO list must have length 1 before block registration" ); own_wallet_state - .update_wallet_state_with_new_block(&mutator_set_accumulator, &block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await?; assert_eq!( 1, @@ -596,7 +597,10 @@ mod wallet_tests { ) .unwrap(); own_wallet_state - .update_wallet_state_with_new_block(&MutatorSetAccumulator::default(), &block_1) + .update_wallet_state_with_new_block( + &genesis_block.kernel.body.mutator_set_accumulator, + &block_1, + ) .await?; // Verify that the allocater returns a sane amount @@ -794,7 +798,7 @@ mod wallet_tests { "Premine must have non-zero synced balance" ); - let previous_msa = MutatorSetAccumulator::default(); + let previous_msa = genesis_block.kernel.body.mutator_set_accumulator.clone(); let (mut block_1, _, _) = make_mock_block(&genesis_block, None, own_address); let receiver_data_12_to_other = UtxoReceiverData { diff --git a/src/models/state/wallet/wallet_state.rs b/src/models/state/wallet/wallet_state.rs index 14b6585cb..cbe4f9d16 100644 --- a/src/models/state/wallet/wallet_state.rs +++ b/src/models/state/wallet/wallet_state.rs @@ -759,7 +759,7 @@ mod tests { .wallet_db .monitored_utxos() .len(); - let mut mutator_set_accumulator = MutatorSetAccumulator::::default(); + let mut mutator_set_accumulator = genesis_block.kernel.body.mutator_set_accumulator.clone(); assert!( monitored_utxos_count_init.is_zero(), "Monitored UTXO list must be empty at init" From 2947dd0e7be8b888f88c6512059f91a60a7fe293 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 11:56:38 +0100 Subject: [PATCH 10/29] fix: `accumulate_transaction` --- src/models/blockchain/block/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index cf39ce666..13a992689 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -218,15 +218,16 @@ impl Block { transaction.kernel.timestamp.value(), ), )); - let new_transaction = self.kernel.body.transaction.clone().merge_with(transaction); // accumulate - let additions = new_transaction.kernel.outputs.clone(); - let removals = new_transaction.kernel.inputs.clone(); let mut next_mutator_set_accumulator = self.kernel.body.mutator_set_accumulator.clone(); - let mutator_set_update = MutatorSetUpdate::new(removals, additions); + let mutator_set_update = MutatorSetUpdate::new( + transaction.kernel.inputs.clone(), + transaction.kernel.outputs.clone(), + ); + let new_transaction = self.kernel.body.transaction.clone().merge_with(transaction); // Apply the mutator set update to get the `next_mutator_set_accumulator` mutator_set_update .apply(&mut next_mutator_set_accumulator) From a84a7c26749a6caf86c19ad36d4b1f59c3ebe3b3 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 12:05:15 +0100 Subject: [PATCH 11/29] fix: `wallet_state_constructor_with_genesis_block_test` --- src/models/state/wallet/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/models/state/wallet/mod.rs b/src/models/state/wallet/mod.rs index 636de4f89..b361f2aed 100644 --- a/src/models/state/wallet/mod.rs +++ b/src/models/state/wallet/mod.rs @@ -414,9 +414,10 @@ mod wallet_tests { let (nb, _coinbase_utxo, _sender_randomness) = make_mock_block(&previous_block, None, other_receiver_address); next_block = nb; - let mutator_set_accumulator = next_block.kernel.body.mutator_set_accumulator.clone(); + let current_mutator_set_accumulator = + previous_block.kernel.body.mutator_set_accumulator.clone(); wallet_state_premine_recipient - .update_wallet_state_with_new_block(&mutator_set_accumulator, &next_block) + .update_wallet_state_with_new_block(¤t_mutator_set_accumulator, &next_block) .await?; } From 4571d7c65061a8083d0ff1cd4d3f3dad1883143c Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 13:04:02 +0100 Subject: [PATCH 12/29] fix: `get_ancestor_block_digests` --- src/models/state/archival_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 820bbcab0..6db723300 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -559,7 +559,7 @@ impl ArchivalState { if count == 0 { break; } - ret.push(Hash::hash(&parent)); + ret.push(parent_digest); parent_digest = parent.prev_block_digest; count -= 1; } From 4a86a73755edbd521a00254332ecfb6c8ccac739 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 13:14:24 +0100 Subject: [PATCH 13/29] implement `MastHash` for `BlockHeader` --- src/models/blockchain/block/block_header.rs | 58 +++++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index ca4ffd5e4..690630aaf 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -1,12 +1,10 @@ -use crate::models::blockchain::shared::Hash; use crate::prelude::twenty_first; +use crate::models::consensus::mast_hash::HasDiscriminant; +use crate::models::consensus::mast_hash::MastHash; use get_size::GetSize; use serde::{Deserialize, Serialize}; use std::fmt::Display; -use tasm_lib::twenty_first::prelude::AlgebraicHasher; -use tasm_lib::twenty_first::util_types::merkle_tree::{CpuParallel, MerkleTree}; -use tasm_lib::twenty_first::util_types::merkle_tree_maker::MerkleTreeMaker; use twenty_first::shared_math::bfield_codec::BFieldCodec; use twenty_first::shared_math::digest::Digest; @@ -66,9 +64,45 @@ impl Display for BlockHeader { } } -impl BlockHeader { - fn mast_sequences(&self) -> [Vec; 16] { - [ +pub enum BlockHeaderField { + Version, + Height, + MutatorSetHash, + PrevBlockDigest, + Timestamp, + Nonce, + MaxBlockSize, + ProofOfWorkLine, + ProofOfWorkFamily, + Difficulty, + BlockBodyMerkleRoot, + Uncles, +} + +impl HasDiscriminant for BlockHeaderField { + fn discriminant(&self) -> usize { + match self { + BlockHeaderField::Version => 0, + BlockHeaderField::Height => 1, + BlockHeaderField::MutatorSetHash => 2, + BlockHeaderField::PrevBlockDigest => 3, + BlockHeaderField::Timestamp => 4, + BlockHeaderField::Nonce => 5, + BlockHeaderField::MaxBlockSize => 6, + BlockHeaderField::ProofOfWorkLine => 7, + BlockHeaderField::ProofOfWorkFamily => 8, + BlockHeaderField::Difficulty => 9, + BlockHeaderField::BlockBodyMerkleRoot => 10, + BlockHeaderField::Uncles => 11, + } + } +} + +impl MastHash for BlockHeader { + type FieldEnum = BlockHeaderField; + + fn mast_sequences(&self) -> Vec> { + vec![ self.version.encode(), self.height.encode(), self.mutator_set_hash.encode(), @@ -87,16 +121,6 @@ impl BlockHeader { vec![], ] } - - fn merkle_tree(&self) -> MerkleTree { - let sequences = self.mast_sequences(); - let digests = sequences.map(|seq| Hash::hash_varlen(&seq)); - CpuParallel::from_digests(&digests).unwrap() - } - - pub(crate) fn mast_hash(&self) -> Digest { - self.merkle_tree().root() - } } #[cfg(test)] From 258fdf6a915be4106fab9267066d668d31b21a7d Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 13:34:08 +0100 Subject: [PATCH 14/29] refactor: `get_children_blocks` now takes `Digest` --- src/models/state/archival_state.rs | 22 +++++++++++++++------- src/peer_loop.rs | 5 ++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 6db723300..fbfc35be6 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -7,7 +7,7 @@ use std::fs; use std::io::{Seek, SeekFrom, Write}; use std::ops::DerefMut; use std::path::PathBuf; -use tracing::debug; +use tracing::{debug, warn}; use twenty_first::amount::u32s::U32s; use twenty_first::shared_math::digest::Digest; use twenty_first::storage::level_db::DB; @@ -482,18 +482,25 @@ impl ArchivalState { } } - pub async fn get_children_blocks(&self, parent_block_header: &BlockHeader) -> Vec { + pub async fn get_children_blocks(&self, parent_block_digest: Digest) -> Vec { + // get header + let parent_block_header = match self.get_block_header(parent_block_digest).await { + Some(header) => header, + None => { + warn!("Querying for children of unknown parent block digest."); + return vec![]; + } + }; // Get all blocks with height n + 1 let blocks_from_childrens_generation: Vec = self .block_height_to_block_headers(parent_block_header.height.next()) .await; // Filter out those that don't have the right parent - let parent_block_header_digest = Hash::hash(parent_block_header); blocks_from_childrens_generation .into_iter() .filter(|child_block_header| { - child_block_header.prev_block_digest == parent_block_header_digest + child_block_header.prev_block_digest == parent_block_digest }) .collect() } @@ -727,6 +734,7 @@ mod archival_state_tests { use crate::models::blockchain::transaction::utxo::LockScript; use crate::models::blockchain::transaction::PublicAnnouncement; use crate::models::blockchain::transaction::{amount::Amount, utxo::Utxo}; + use crate::models::consensus::mast_hash::MastHash; use crate::models::state::archival_state::ArchivalState; use crate::models::state::wallet::utxo_notification_pool::UtxoNotifier; use crate::models::state::wallet::WalletSecret; @@ -2702,7 +2710,7 @@ mod archival_state_tests { // Test `get_children_blocks` let children_of_mock_block_1 = archival_state - .get_children_blocks(&mock_block_1.kernel.header) + .get_children_blocks(mock_block_1.kernel.mast_hash()) .await; assert_eq!(1, children_of_mock_block_1.len()); assert_eq!(mock_block_2.kernel.header, children_of_mock_block_1[0]); @@ -2712,8 +2720,8 @@ mod archival_state_tests { .get_ancestor_block_digests(mock_block_2.hash(), 10) .await; assert_eq!(2, ancestor_digests.len()); - assert_eq!(Hash::hash(&mock_block_1.kernel.header), ancestor_digests[0]); - assert_eq!(Hash::hash(&genesis.kernel.header), ancestor_digests[1]); + assert_eq!(mock_block_1.kernel.mast_hash(), ancestor_digests[0]); + assert_eq!(genesis.kernel.mast_hash(), ancestor_digests[1]); Ok(()) } diff --git a/src/peer_loop.rs b/src/peer_loop.rs index 8ec85d65e..d7a6723d6 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::twenty_first; use super::models::blockchain::shared::Hash; @@ -495,12 +496,11 @@ impl PeerLoopHandler { let mut returned_blocks: Vec = Vec::with_capacity(responded_batch_size); - let mut parent_block_header: BlockHeader = peers_most_canonical_block.kernel.header; while returned_blocks.len() < responded_batch_size { let children = global_state .chain .archival_state() - .get_children_blocks(&parent_block_header) + .get_children_blocks(peers_most_canonical_block.kernel.mast_hash()) .await; if children.is_empty() { break; @@ -530,7 +530,6 @@ impl PeerLoopHandler { .await? .unwrap(); - parent_block_header = header_of_canonical_child; returned_blocks.push(canonical_child.into()); } From c2b5656e7cee3c0fed745f7ae669258e84ebaccb Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 13:54:51 +0100 Subject: [PATCH 15/29] fix: mock blocks should have some proof So: populate proof with empty vec. --- src/tests/shared.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 66202eb3b..a63472c2c 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -28,6 +28,7 @@ use std::{ sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; +use tasm_lib::triton_vm::proof::Proof; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use tokio::sync::{broadcast, mpsc}; use tokio_serde::{formats::SymmetricalBincode, Serializer}; @@ -973,7 +974,7 @@ pub fn make_mock_block( }; ( - Block::new(block_header, block_body, None), + Block::new(block_header, block_body, Some(Proof(vec![]))), coinbase_utxo, coinbase_output_randomness, ) From 665a0dd67c68fa6fcbc7c62f70f07feeaf6d651e Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Mon, 29 Jan 2024 15:36:45 +0100 Subject: [PATCH 16/29] fix: index blocks by digest not header --- src/models/state/archival_state.rs | 163 +++++++++++++++------- src/models/state/mod.rs | 22 +-- src/models/state/wallet/monitored_utxo.rs | 19 +-- src/peer_loop.rs | 39 +++--- 4 files changed, 144 insertions(+), 99 deletions(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index fbfc35be6..0768323d1 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -7,11 +7,11 @@ use std::fs; use std::io::{Seek, SeekFrom, Write}; use std::ops::DerefMut; use std::path::PathBuf; +use tasm_lib::twenty_first::util_types::emojihash_trait::Emojihash; use tracing::{debug, warn}; use twenty_first::amount::u32s::U32s; use twenty_first::shared_math::digest::Digest; use twenty_first::storage::level_db::DB; -use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use twenty_first::util_types::mmr::mmr_trait::Mmr; use twenty_first::util_types::storage_schema::traits::*; @@ -457,32 +457,34 @@ impl ArchivalState { &self, block_height: BlockHeight, ) -> Vec { - let maybe_digests = self - .block_index_db + let block_digests = self.block_height_to_block_digests(block_height).await; + let mut block_headers = vec![]; + for block_digest in block_digests.into_iter() { + let block = self + .block_index_db + .get(BlockIndexKey::Block(block_digest)) + .await + .map(|x| x.as_block_record()) + .unwrap(); + block_headers.push(block.block_header); + } + + block_headers + } + + /// Return the digests of the known blocks at a specific height + pub async fn block_height_to_block_digests(&self, block_height: BlockHeight) -> Vec { + self.block_index_db .get(BlockIndexKey::Height(block_height)) .await - .map(|x| x.as_height_record()); - - match maybe_digests { - Some(block_digests) => { - let mut block_headers = vec![]; - for block_digest in block_digests { - let block_header = self - .block_index_db - .get(BlockIndexKey::Block(block_digest)) - .await - .map(|x| x.as_block_record()) - .unwrap(); - block_headers.push(block_header.block_header); - } - - block_headers - } - None => vec![], - } + .map(|x| x.as_height_record()) + .unwrap_or_else(Vec::new) } - pub async fn get_children_blocks(&self, parent_block_digest: Digest) -> Vec { + pub async fn get_children_block_headers( + &self, + parent_block_digest: Digest, + ) -> Vec { // get header let parent_block_header = match self.get_block_header(parent_block_digest).await { Some(header) => header, @@ -505,24 +507,74 @@ impl ArchivalState { .collect() } + pub async fn get_children_block_digests(&self, parent_block_digest: Digest) -> Vec { + // get header + let parent_block_header = match self.get_block_header(parent_block_digest).await { + Some(header) => header, + None => { + warn!("Querying for children of unknown parent block digest."); + return vec![]; + } + }; + // Get all blocks with height n + 1 + let children_digests = self + .block_height_to_block_digests(parent_block_header.height.next()) + .await; + let mut downstream_children = vec![]; + for child_digest in children_digests { + let child_header = + self + .get_block_header(child_digest) + .await + .unwrap_or_else( + || panic!( + "Cannot get block header from digest, even though digest was fetched from height. Digest: {}/{}", + child_digest, + child_digest.emojihash() + ) + ); + if child_header.prev_block_digest == parent_block_digest { + downstream_children.push(child_digest); + } + } + + downstream_children + } + /// Return a boolean indicating if block belongs to most canonical chain pub async fn block_belongs_to_canonical_chain( &self, - block_header: &BlockHeader, - tip_header: &BlockHeader, + block_digest: Digest, + tip_digest: Digest, ) -> bool { - let block_header_digest = Hash::hash(block_header); - let tip_header_digest = Hash::hash(tip_header); + let block_header = self + .get_block_header(block_digest) + .await + .unwrap_or_else(|| { + panic!( + "Could not get block header by digest: {}/{}", + block_digest, + block_digest.emojihash() + ) + }); + let tip_header = self + .get_block_header(block_digest) + .await + .unwrap_or_else(|| { + panic!( + "Could not get block header by digest: {}/{}", + tip_digest, + tip_digest.emojihash() + ) + }); // If block is tip or parent to tip, then block belongs to canonical chain - if tip_header_digest == block_header_digest - || tip_header.prev_block_digest == block_header_digest - { + if tip_digest == block_digest || tip_header.prev_block_digest == block_digest { return true; } // If block is genesis block, it belongs to the canonical chain - if block_header_digest == self.genesis_block.hash() { + if block_digest == self.genesis_block.hash() { return true; } @@ -543,7 +595,7 @@ impl ArchivalState { } // Find the path from block to tip and check if this involves stepping back - let (backwards, _, _) = self.find_path(block_header_digest, tip_header_digest).await; + let (backwards, _, _) = self.find_path(block_digest, tip_digest).await; backwards.is_empty() } @@ -2002,7 +2054,10 @@ mod archival_state_tests { let genesis = *archival_state.genesis_block.clone(); assert!( archival_state - .block_belongs_to_canonical_chain(&genesis.kernel.header, &genesis.kernel.header) + .block_belongs_to_canonical_chain( + genesis.kernel.mast_hash(), + genesis.kernel.mast_hash() + ) .await, "Genesis block is always part of the canonical chain, tip" ); @@ -2016,8 +2071,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &genesis.kernel.header, - &mock_block_1.kernel.header + genesis.kernel.mast_hash(), + mock_block_1.kernel.mast_hash() ) .await, "Genesis block is always part of the canonical chain, tip parent" @@ -2025,8 +2080,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &mock_block_1.kernel.header, - &mock_block_1.kernel.header + mock_block_1.kernel.mast_hash(), + mock_block_1.kernel.mast_hash() ) .await, "Tip block is always part of the canonical chain" @@ -2055,8 +2110,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_4_a.kernel.header + block.kernel.mast_hash(), + mock_block_4_a.kernel.mast_hash() ) .await, "only chain {} is canonical", @@ -2069,8 +2124,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &genesis.kernel.header, - &mock_block_4_a.kernel.header + genesis.kernel.mast_hash(), + mock_block_4_a.kernel.mast_hash() ) .await, "Genesis block is always part of the canonical chain, block height is four" @@ -2103,8 +2158,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_4_a.kernel.header + block.kernel.mast_hash(), + mock_block_4_a.kernel.mast_hash() ) .await, "canonical chain {} is canonical", @@ -2128,8 +2183,8 @@ mod archival_state_tests { assert!( !archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_4_a.kernel.header + block.kernel.mast_hash(), + mock_block_4_a.kernel.mast_hash() ) .await, "Stale chain {} is not canonical", @@ -2213,8 +2268,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_6_d.kernel.header + block.kernel.mast_hash(), + mock_block_6_d.kernel.mast_hash() ) .await, "canonical chain {} is canonical, complicated", @@ -2247,8 +2302,8 @@ mod archival_state_tests { assert!( !archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_6_d.kernel.header + block.kernel.mast_hash(), + mock_block_6_d.kernel.mast_hash() ) .await, "Stale chain {} is not canonical", @@ -2286,8 +2341,8 @@ mod archival_state_tests { assert!( !archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_6_b.kernel.header + block.kernel.mast_hash(), + mock_block_6_b.kernel.mast_hash() ) .await, "Stale chain {} is not canonical", @@ -2312,8 +2367,8 @@ mod archival_state_tests { assert!( archival_state .block_belongs_to_canonical_chain( - &block.kernel.header, - &mock_block_6_b.kernel.header + block.kernel.mast_hash(), + mock_block_6_b.kernel.mast_hash() ) .await, "canonical chain {} is canonical, complicated", @@ -2710,7 +2765,7 @@ mod archival_state_tests { // Test `get_children_blocks` let children_of_mock_block_1 = archival_state - .get_children_blocks(mock_block_1.kernel.mast_hash()) + .get_children_block_headers(mock_block_1.kernel.mast_hash()) .await; assert_eq!(1, children_of_mock_block_1.len()); assert_eq!(mock_block_2.kernel.header, children_of_mock_block_1[0]); diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index af1280e6c..7af8e0d1f 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -1,3 +1,4 @@ +use crate::models::consensus::mast_hash::MastHash; use crate::prelude::twenty_first; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; @@ -894,12 +895,13 @@ impl GlobalState { ) } - let current_tip = self.chain.light_state().header(); + let current_tip_header = self.chain.light_state().header(); + let current_tip_digest = self.chain.light_state().kernel.mast_hash(); let current_tip_info: (Digest, Duration, BlockHeight) = ( - Hash::hash(current_tip), - Duration::from_millis(current_tip.timestamp.value()), - current_tip.height, + Hash::hash(current_tip_header), + Duration::from_millis(current_tip_header.timestamp.value()), + current_tip_header.height, ); let mut updates = std::collections::BTreeMap::new(); @@ -928,11 +930,11 @@ impl GlobalState { // if it was confirmed in block that is now abandoned, // and if that block is older than threshold. if let Some((_, _, block_height_confirmed)) = mutxo.confirmed_in_block { - let depth = current_tip.height - block_height_confirmed + 1; + let depth = current_tip_header.height - block_height_confirmed + 1; let abandoned = depth >= block_depth_threshhold as i128 && mutxo - .was_abandoned(current_tip, self.chain.archival_state()) + .was_abandoned(current_tip_digest, self.chain.archival_state()) .await; if abandoned { @@ -1449,7 +1451,7 @@ mod global_state_tests { !monitored_utxos .get(0) .was_abandoned( - &parent_block.kernel.header, + parent_block.kernel.mast_hash(), global_state.chain.archival_state() ) .await @@ -1458,7 +1460,7 @@ mod global_state_tests { monitored_utxos .get(1) .was_abandoned( - &parent_block.kernel.header, + parent_block.kernel.mast_hash(), global_state.chain.archival_state() ) .await @@ -1663,7 +1665,7 @@ mod global_state_tests { !monitored_utxos .get(0) .was_abandoned( - &fork_c_block.kernel.header, + fork_c_block.kernel.mast_hash(), global_state.chain.archival_state() ) .await @@ -1672,7 +1674,7 @@ mod global_state_tests { monitored_utxos .get(1) .was_abandoned( - &fork_c_block.kernel.header, + fork_c_block.kernel.mast_hash(), global_state.chain.archival_state() ) .await diff --git a/src/models/state/wallet/monitored_utxo.rs b/src/models/state/wallet/monitored_utxo.rs index aaaea4417..e0661ab2e 100644 --- a/src/models/state/wallet/monitored_utxo.rs +++ b/src/models/state/wallet/monitored_utxo.rs @@ -3,10 +3,7 @@ use crate::prelude::twenty_first; use std::{collections::VecDeque, time::Duration}; use crate::{ - models::{ - blockchain::block::{block_header::BlockHeader, block_height::BlockHeight}, - state::archival_state::ArchivalState, - }, + models::{blockchain::block::block_height::BlockHeight, state::archival_state::ArchivalState}, util_types::mutator_set::ms_membership_proof::MsMembershipProof, Hash, }; @@ -80,19 +77,11 @@ impl MonitoredUtxo { } /// Returns true if the MUTXO was abandoned - pub async fn was_abandoned( - &self, - tip_header: &BlockHeader, - archival_state: &ArchivalState, - ) -> bool { + pub async fn was_abandoned(&self, tip_digest: Digest, archival_state: &ArchivalState) -> bool { match self.confirmed_in_block { - Some((confirm_block, _, _)) => { - let confirm_block_header = archival_state - .get_block_header(confirm_block) - .await - .unwrap(); + Some((confirm_block_digest, _, _)) => { !archival_state - .block_belongs_to_canonical_chain(&confirm_block_header, tip_header) + .block_belongs_to_canonical_chain(confirm_block_digest, tip_digest) .await } None => false, diff --git a/src/peer_loop.rs b/src/peer_loop.rs index d7a6723d6..a8924c789 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -3,7 +3,6 @@ use crate::prelude::twenty_first; use super::models::blockchain::shared::Hash; use crate::connect_to_peers::close_peer_connected_callback; -use crate::models::blockchain::block::block_header::BlockHeader; use crate::models::blockchain::block::block_height::BlockHeight; use crate::models::blockchain::block::transfer_block::TransferBlock; use crate::models::blockchain::block::Block; @@ -451,14 +450,14 @@ impl PeerLoopHandler { let global_state = self.global_state_lock.lock_guard().await; - let tip_header = &global_state.chain.light_state().kernel.header; + let tip_digest = global_state.chain.light_state().kernel.mast_hash(); if global_state .chain .archival_state() .block_belongs_to_canonical_chain( - &block_candidate.kernel.header, - tip_header, + block_candidate.kernel.mast_hash(), + tip_digest, ) .await { @@ -490,7 +489,7 @@ impl PeerLoopHandler { / 2, ); let global_state = self.global_state_lock.lock_guard().await; - let tip_header = &global_state.chain.light_state().kernel.header; + let tip_digest = global_state.chain.light_state().kernel.mast_hash(); let responded_batch_size = cmp::max(responded_batch_size, MINIMUM_BLOCK_BATCH_SIZE); let mut returned_blocks: Vec = @@ -500,20 +499,20 @@ impl PeerLoopHandler { let children = global_state .chain .archival_state() - .get_children_blocks(peers_most_canonical_block.kernel.mast_hash()) + .get_children_block_digests(peers_most_canonical_block.kernel.mast_hash()) .await; if children.is_empty() { break; } let header_of_canonical_child = if children.len() == 1 { - children[0].clone() + children[0] } else { - let mut canonical: BlockHeader = children[0].clone(); + let mut canonical = children[0]; for child in children { if global_state .chain .archival_state() - .block_belongs_to_canonical_chain(&child, tip_header) + .block_belongs_to_canonical_chain(child, tip_digest) .await { canonical = child; @@ -682,17 +681,17 @@ impl PeerLoopHandler { PeerMessage::BlockRequestByHeight(block_height) => { debug!("Got BlockRequestByHeight of height {}", block_height); - let block_headers: Vec = self + let block_digests = self .global_state_lock .lock_guard() .await .chain .archival_state() - .block_height_to_block_headers(block_height) + .block_height_to_block_digests(block_height) .await; - debug!("Found {} blocks", block_headers.len()); + debug!("Found {} blocks", block_digests.len()); - if block_headers.is_empty() { + if block_digests.is_empty() { warn!("Got block request by height for unknown block"); self.punish(PeerSanctionReason::BlockRequestUnknownHeight) .await?; @@ -700,18 +699,18 @@ impl PeerLoopHandler { } // If more than one block is found, we need to find the one that's canonical - let mut canonical_chain_block_header = block_headers[0].clone(); - if block_headers.len() > 1 { + let mut canonical_chain_block_digest = block_digests[0]; + if block_digests.len() > 1 { let global_state = self.global_state_lock.lock_guard().await; - let tip_header = &global_state.chain.light_state().kernel.header; - for block_header in block_headers { + let tip_digest = global_state.chain.light_state().kernel.mast_hash(); + for block_digest in block_digests { if global_state .chain .archival_state() - .block_belongs_to_canonical_chain(&block_header, tip_header) + .block_belongs_to_canonical_chain(block_digest, tip_digest) .await { - canonical_chain_block_header = block_header; + canonical_chain_block_digest = block_digest; } } } @@ -722,7 +721,7 @@ impl PeerLoopHandler { .await .chain .archival_state() - .get_block(Hash::hash(&canonical_chain_block_header)) + .get_block(canonical_chain_block_digest) .await? .unwrap(); let block_response: PeerMessage = From feca24d6a5e5b36f032dfbd9de552e30422b640c Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 12:05:44 +0100 Subject: [PATCH 17/29] track flaky test `allow_multiple_inputs_and_outputs_in_block` - Add debug print statements in test. - In some mutator set method, turn `unwrap` into `unwrap_or_else` with `panic!` in order to display debug info. --- src/models/state/archival_state.rs | 21 +++++++++++++++++++ .../mutator_set/mutator_set_kernel.rs | 11 +++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 0768323d1..1a2d6257c 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -1451,6 +1451,13 @@ mod archival_state_tests { assert!(block_1.is_valid(&genesis_block)); } + println!("Accumulated transaction into block_1."); + println!( + "Transaction has {} inputs (removal records) and {} outputs (addition records)", + block_1.kernel.body.transaction.kernel.inputs.len(), + block_1.kernel.body.transaction.kernel.outputs.len() + ); + // Update chain states for state_lock in [&genesis_state_lock, &alice_state_lock, &bob_state_lock] { let mut state = state_lock.lock_guard_mut().await; @@ -1632,6 +1639,20 @@ mod archival_state_tests { let (mut block_2, cb_utxo_block_2, cb_sender_randomness_block_2) = make_mock_block_with_valid_pow(&block_1, None, genesis_spending_key.to_address()); block_2.accumulate_transaction(tx_from_alice); + assert_eq!(2, block_2.kernel.body.transaction.kernel.inputs.len()); + assert_eq!(3, block_2.kernel.body.transaction.kernel.outputs.len()); + + // This test is flaky! + // It fails roughly every 3 out of 10 runs. If you run with `-- --nocapture` then + // (on Alan's machine) it runs with a 100% success rate. But doing that *and* + // commenting out the next two print statements boosts the failure rate again. + println!("accumulated Alice's transaction into block; number of inputs: {}; number of outputs: {}", block_2.kernel.body.transaction.kernel.inputs.len(), block_2.kernel.body.transaction.kernel.outputs.len()); + println!( + "Transaction from Bob has {} inputs and {} outputs", + tx_from_bob.kernel.inputs.len(), + tx_from_bob.kernel.outputs.len() + ); + block_2.accumulate_transaction(tx_from_bob); // Sanity checks diff --git a/src/util_types/mutator_set/mutator_set_kernel.rs b/src/util_types/mutator_set/mutator_set_kernel.rs index 7bc3d150a..400676ed8 100644 --- a/src/util_types/mutator_set/mutator_set_kernel.rs +++ b/src/util_types/mutator_set/mutator_set_kernel.rs @@ -183,7 +183,16 @@ impl> MutatorSetKernel { } // If chunk index is not in the active part, insert the index into the relevant chunk - let relevant_chunk = new_target_chunks.dictionary.get_mut(&chunk_index).unwrap(); + let new_target_chunks_clone = new_target_chunks.clone(); + let relevant_chunk = new_target_chunks + .dictionary + .get_mut(&chunk_index) + .unwrap_or_else(|| { + panic!( + "Can't get chunk index {chunk_index} from dictionary! dictionary: {:?}", + new_target_chunks_clone.dictionary + ) + }); for index in indices { let relative_index = (index % CHUNK_SIZE as u128) as u32; relevant_chunk.1.insert(relative_index); From c28755daa471ca7c33f029464c1299f9046c4765 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 14:36:57 +0100 Subject: [PATCH 18/29] fix `block_belongs_to_canonical_chain` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem was using `block_digest` instead of `tip_digest` to lookup the tip header. Co-authored-by: Thorkil Værge --- src/models/state/archival_state.rs | 22 ++++++++++---------- src/peer_loop.rs | 32 ++++++++++++++++++------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/models/state/archival_state.rs b/src/models/state/archival_state.rs index 1a2d6257c..fb348b985 100644 --- a/src/models/state/archival_state.rs +++ b/src/models/state/archival_state.rs @@ -507,6 +507,8 @@ impl ArchivalState { .collect() } + /// Get all immediate children of the given block, no grandchildren or higher-order + /// descendants. pub async fn get_children_block_digests(&self, parent_block_digest: Digest) -> Vec { // get header let parent_block_header = match self.get_block_header(parent_block_digest).await { @@ -557,16 +559,13 @@ impl ArchivalState { block_digest.emojihash() ) }); - let tip_header = self - .get_block_header(block_digest) - .await - .unwrap_or_else(|| { - panic!( - "Could not get block header by digest: {}/{}", - tip_digest, - tip_digest.emojihash() - ) - }); + let tip_header = self.get_block_header(tip_digest).await.unwrap_or_else(|| { + panic!( + "Could not get block header by digest: {}/{}", + tip_digest, + tip_digest.emojihash() + ) + }); // If block is tip or parent to tip, then block belongs to canonical chain if tip_digest == block_digest || tip_header.prev_block_digest == block_digest { @@ -780,6 +779,7 @@ impl ArchivalState { #[cfg(test)] mod archival_state_tests { + use super::*; use crate::config_models::network::Network; @@ -2135,7 +2135,7 @@ mod archival_state_tests { mock_block_4_a.kernel.mast_hash() ) .await, - "only chain {} is canonical", + "block {} does not belong to canonical chain", i ); dag_walker_leash_prop(block.hash(), mock_block_4_a.hash(), &archival_state).await; diff --git a/src/peer_loop.rs b/src/peer_loop.rs index a8924c789..dda775ae5 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -1,7 +1,6 @@ use crate::models::consensus::mast_hash::MastHash; use crate::prelude::twenty_first; -use super::models::blockchain::shared::Hash; use crate::connect_to_peers::close_peer_connected_callback; use crate::models::blockchain::block::block_height::BlockHeight; use crate::models::blockchain::block::transfer_block::TransferBlock; @@ -26,7 +25,6 @@ use tokio::select; use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error, info, warn}; use twenty_first::shared_math::digest::Digest; -use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; const STANDARD_BLOCK_BATCH_SIZE: usize = 50; const MAX_PEER_LIST_LENGTH: usize = 10; @@ -429,11 +427,14 @@ impl PeerLoopHandler { } Ok(false) } - PeerMessage::BlockRequestBatch(most_canonical_digests, requested_batch_size) => { + PeerMessage::BlockRequestBatch( + peers_suggested_starting_points, + requested_batch_size, + ) => { // Find the block that the peer is requesting to start from let mut peers_most_canonical_block: Option = None; - for digest in most_canonical_digests { + for digest in peers_suggested_starting_points { debug!("Looking up block {} in batch request", digest); let block_candidate = self .global_state_lock @@ -477,8 +478,8 @@ impl PeerLoopHandler { } }; - // Get the relevant blocks, from the descendant of the peer's most canonical block - // to that height plus the batch size. + // Get the relevant blocks, at most batch size many, descending from the + // peer's most canonical block. let responded_batch_size = cmp::min( requested_batch_size, self.global_state_lock @@ -495,20 +496,22 @@ impl PeerLoopHandler { let mut returned_blocks: Vec = Vec::with_capacity(responded_batch_size); + let mut current_digest = peers_most_canonical_block.kernel.mast_hash(); while returned_blocks.len() < responded_batch_size { let children = global_state .chain .archival_state() - .get_children_block_digests(peers_most_canonical_block.kernel.mast_hash()) + .get_children_block_digests(current_digest) .await; + if children.is_empty() { break; } - let header_of_canonical_child = if children.len() == 1 { + let canonical_child_digest = if children.len() == 1 { children[0] } else { let mut canonical = children[0]; - for child in children { + for child in children.into_iter().skip(1) { if global_state .chain .archival_state() @@ -522,14 +525,17 @@ impl PeerLoopHandler { canonical }; + // get block and append to list let canonical_child: Block = global_state .chain .archival_state() - .get_block(Hash::hash(&header_of_canonical_child)) + .get_block(canonical_child_digest) .await? .unwrap(); - returned_blocks.push(canonical_child.into()); + + // prepare for next iteration + current_digest = canonical_child_digest; } debug!( @@ -537,8 +543,8 @@ impl PeerLoopHandler { returned_blocks.len() ); - peer.send(PeerMessage::BlockResponseBatch(returned_blocks)) - .await?; + let response = PeerMessage::BlockResponseBatch(returned_blocks); + peer.send(response).await?; Ok(false) } From a99b16a984b164675ae8764da0f4778690cd95ef Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 15:03:03 +0100 Subject: [PATCH 19/29] feat: allow batch-queries of blocks with out-of-order starting points --- src/peer_loop.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/src/peer_loop.rs b/src/peer_loop.rs index dda775ae5..c972ddc82 100644 --- a/src/peer_loop.rs +++ b/src/peer_loop.rs @@ -432,7 +432,7 @@ impl PeerLoopHandler { requested_batch_size, ) => { // Find the block that the peer is requesting to start from - let mut peers_most_canonical_block: Option = None; + let mut peers_latest_canonical_block: Option = None; for digest in peers_suggested_starting_points { debug!("Looking up block {} in batch request", digest); @@ -462,15 +462,25 @@ impl PeerLoopHandler { ) .await { - peers_most_canonical_block = Some(block_candidate); + peers_latest_canonical_block = match peers_latest_canonical_block { + None => Some(block_candidate), + Some(running_latest_block) => { + if running_latest_block.kernel.header.height + < block_candidate.kernel.header.height + { + Some(block_candidate) + } else { + Some(running_latest_block) + } + } + }; debug!("Found block in canonical chain: {}", digest); - break; } } } - let peers_most_canonical_block = match peers_most_canonical_block { - Some(mcb) => mcb, + let peers_latest_canonical_block = match peers_latest_canonical_block { + Some(plcb) => plcb, None => { self.punish(PeerSanctionReason::BatchBlocksUnknownRequest) .await?; @@ -496,7 +506,7 @@ impl PeerLoopHandler { let mut returned_blocks: Vec = Vec::with_capacity(responded_batch_size); - let mut current_digest = peers_most_canonical_block.kernel.mast_hash(); + let mut current_digest = peers_latest_canonical_block.kernel.mast_hash(); while returned_blocks.len() < responded_batch_size { let children = global_state .chain @@ -1494,7 +1504,7 @@ mod peer_loop_tests { #[traced_test] #[tokio::test] - async fn block_request_batch_test() -> Result<()> { + async fn block_request_batch_in_order_test() -> Result<()> { // Scenario: A fork began at block 2, node knows two blocks of height 2 and two of height 3. // A peer requests a batch of blocks starting from block 1. Ensure that the correct blocks // are returned. @@ -1582,6 +1592,69 @@ mod peer_loop_tests { Ok(()) } + #[traced_test] + #[tokio::test] + async fn block_request_batch_out_of_order_test() -> Result<()> { + // Scenario: Same as above, but the peer supplies their hashes in a wrong order. + // Ensure that the correct blocks are returned, in the right order. + let network = Network::Alpha; + let (_peer_broadcast_tx, from_main_rx_clone, to_main_tx, _to_main_rx1, state_lock, hsd) = + get_test_genesis_setup(network, 0).await?; + let mut global_state_mut = state_lock.lock_guard_mut().await; + let genesis_block: Block = global_state_mut + .chain + .archival_state() + .get_latest_block() + .await; + let peer_address = get_dummy_socket_address(0); + let a_wallet_secret = WalletSecret::new_random(); + let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); + let (block_1, _, _) = + make_mock_block_with_valid_pow(&genesis_block, None, a_recipient_address); + let (block_2_a, _, _) = make_mock_block_with_valid_pow(&block_1, None, a_recipient_address); + let (block_3_a, _, _) = + make_mock_block_with_valid_pow(&block_2_a, None, a_recipient_address); // <--- canonical + let (block_2_b, _, _) = make_mock_block_with_valid_pow(&block_1, None, a_recipient_address); + let (block_3_b, _, _) = + make_mock_block_with_valid_pow(&block_2_b, None, a_recipient_address); + + add_block(&mut global_state_mut, block_1.clone()).await?; + add_block(&mut global_state_mut, block_2_a.clone()).await?; + add_block(&mut global_state_mut, block_3_a.clone()).await?; + add_block(&mut global_state_mut, block_2_b.clone()).await?; + add_block(&mut global_state_mut, block_3_b.clone()).await?; + + drop(global_state_mut); + + // Peer knows block 2_b, verify that canonical chain with 2_a is returned + let mock = Mock::new(vec![ + Action::Read(PeerMessage::BlockRequestBatch( + vec![block_2_b.hash(), genesis_block.hash(), block_1.hash()], + 14, + )), + Action::Write(PeerMessage::BlockResponseBatch(vec![ + block_2_a.into(), + block_3_a.into(), + ])), + Action::Read(PeerMessage::Bye), + ]); + + let peer_loop_handler_2 = PeerLoopHandler::new( + to_main_tx.clone(), + state_lock.clone(), + peer_address, + hsd, + false, + 1, + ); + + peer_loop_handler_2 + .run_wrapper(mock, from_main_rx_clone) + .await?; + + Ok(()) + } + #[traced_test] #[tokio::test] async fn find_canonical_chain_when_multiple_blocks_at_same_height_test() -> Result<()> { From e4f77ccb0f04a9b0375e33c8f779fb1d2c8fec98 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 15:28:25 +0100 Subject: [PATCH 20/29] fix: `prune_abandoned_monitored_utxos` --- src/models/state/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/state/mod.rs b/src/models/state/mod.rs index 7af8e0d1f..e95b5816c 100644 --- a/src/models/state/mod.rs +++ b/src/models/state/mod.rs @@ -899,7 +899,7 @@ impl GlobalState { let current_tip_digest = self.chain.light_state().kernel.mast_hash(); let current_tip_info: (Digest, Duration, BlockHeight) = ( - Hash::hash(current_tip_header), + current_tip_digest, Duration::from_millis(current_tip_header.timestamp.value()), current_tip_header.height, ); From 7e637d168887b16408ff2ef069e20a8f6c3bed8e Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 15:51:00 +0100 Subject: [PATCH 21/29] Move `uncle` field from block header to body --- src/mine_loop.rs | 2 +- src/models/blockchain/block/block_body.rs | 8 ++++++++ src/models/blockchain/block/block_header.rs | 12 +----------- src/models/blockchain/block/mod.rs | 4 ++-- src/tests/shared.rs | 2 +- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 1c9215dd9..b05e9826f 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -65,6 +65,7 @@ fn make_block_template( mutator_set_accumulator: next_mutator_set_accumulator.clone(), lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), block_mmr_accumulator: MmrAccumulator::::new(vec![]), + uncle_blocks: vec![], }; let zero = BFieldElement::zero(); @@ -94,7 +95,6 @@ fn make_block_template( proof_of_work_family: new_pow_line, difficulty, block_body_merkle_root: Hash::hash(&block_body), - uncles: vec![], }; (block_header, block_body) diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index c1f8fff33..10154304f 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -5,6 +5,7 @@ use get_size::GetSize; use serde::{Deserialize, Serialize}; use tasm_lib::twenty_first::shared_math::b_field_element::BFieldElement; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; +use tasm_lib::Digest; use twenty_first::shared_math::bfield_codec::BFieldCodec; use crate::models::blockchain::shared::Hash; @@ -16,6 +17,7 @@ pub enum BlockBodyField { MutatorSetAccumulator, LockFreeMmrAccumulator, BlockMmrAccumulator, + UncleBlocks, } impl HasDiscriminant for BlockBodyField { @@ -25,6 +27,7 @@ impl HasDiscriminant for BlockBodyField { BlockBodyField::MutatorSetAccumulator => 1, BlockBodyField::LockFreeMmrAccumulator => 2, BlockBodyField::BlockMmrAccumulator => 3, + BlockBodyField::UncleBlocks => 4, } } } @@ -46,6 +49,10 @@ pub struct BlockBody { /// All blocks live in an MMR, so that we can efficiently prove that a given block /// lives on the line between the tip and genesis. pub block_mmr_accumulator: MmrAccumulator, + + /// All blocks that lost the block race to an ancestor of this block and have not been + /// listed as uncle before. + pub uncle_blocks: Vec, } impl MastHash for BlockBody { @@ -57,6 +64,7 @@ impl MastHash for BlockBody { self.mutator_set_accumulator.encode(), self.lock_free_mmr_accumulator.encode(), self.block_mmr_accumulator.encode(), + self.uncle_blocks.encode(), ] } } diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 690630aaf..4367ec867 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -42,7 +42,6 @@ pub struct BlockHeader { // This is the difficulty for the *next* block. Unit: expected # hashes pub difficulty: U32s, pub block_body_merkle_root: Digest, - pub uncles: Vec, } impl Display for BlockHeader { @@ -76,7 +75,6 @@ pub enum BlockHeaderField { ProofOfWorkFamily, Difficulty, BlockBodyMerkleRoot, - Uncles, } impl HasDiscriminant for BlockHeaderField { @@ -93,7 +91,6 @@ impl HasDiscriminant for BlockHeaderField { BlockHeaderField::ProofOfWorkFamily => 8, BlockHeaderField::Difficulty => 9, BlockHeaderField::BlockBodyMerkleRoot => 10, - BlockHeaderField::Uncles => 11, } } } @@ -114,19 +111,13 @@ impl MastHash for BlockHeader { self.proof_of_work_family.encode(), self.difficulty.encode(), self.block_body_merkle_root.encode(), - self.uncles.encode(), - vec![], - vec![], - vec![], - vec![], ] } } #[cfg(test)] mod block_header_tests { - use rand::{thread_rng, Rng, RngCore}; - use twenty_first::shared_math::other::random_elements; + use rand::{thread_rng, Rng}; use super::*; @@ -144,7 +135,6 @@ mod block_header_tests { proof_of_work_family: rng.gen(), difficulty: rng.gen(), block_body_merkle_root: rng.gen(), - uncles: random_elements((rng.next_u32() % 3) as usize), } } #[test] diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 13a992689..e69f8d54d 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -168,6 +168,7 @@ impl Block { mutator_set_accumulator: genesis_mutator_set.clone(), block_mmr_accumulator: MmrAccumulator::new(vec![]), lock_free_mmr_accumulator: MmrAccumulator::new(vec![]), + uncle_blocks: vec![], }; let header: BlockHeader = BlockHeader { @@ -186,7 +187,6 @@ impl Block { proof_of_work_family: U32s::zero(), difficulty: MINIMUM_DIFFICULTY.into(), block_body_merkle_root: Hash::hash(&body), - uncles: vec![], }; Self::new(header, body, None) @@ -238,6 +238,7 @@ impl Block { mutator_set_accumulator: next_mutator_set_accumulator.clone(), lock_free_mmr_accumulator: self.kernel.body.lock_free_mmr_accumulator.clone(), block_mmr_accumulator: self.kernel.body.block_mmr_accumulator.clone(), + uncle_blocks: self.kernel.body.uncle_blocks.clone(), }; let block_header = BlockHeader { @@ -252,7 +253,6 @@ impl Block { proof_of_work_family: self.kernel.header.proof_of_work_family, difficulty: self.kernel.header.difficulty, block_body_merkle_root: Hash::hash(&block_body), - uncles: vec![], }; self.kernel.body = block_body; diff --git a/src/tests/shared.rs b/src/tests/shared.rs index a63472c2c..12a33865c 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -951,6 +951,7 @@ pub fn make_mock_block( mutator_set_accumulator: next_mutator_set.clone(), lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), block_mmr_accumulator: MmrAccumulator::::new(vec![]), + uncle_blocks: vec![], }; let block_target_difficulty = previous_block.kernel.header.difficulty; @@ -970,7 +971,6 @@ pub fn make_mock_block( proof_of_work_family: pow_family, difficulty: target_difficulty, block_body_merkle_root: Hash::hash(&block_body), - uncles: vec![], }; ( From 9c850914fb42a98c66c0aa61a19edd76083e1e95 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 17:16:33 +0100 Subject: [PATCH 22/29] Populate and check `block_mmr_accumulator` --- src/mine_loop.rs | 5 ++- src/models/blockchain/block/block_body.rs | 3 +- src/models/blockchain/block/mod.rs | 37 ++++++++++++++++++----- src/tests/shared.rs | 4 ++- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index b05e9826f..0b739e3fb 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -29,6 +29,7 @@ use std::ops::Deref; use std::time::Duration; use std::time::{SystemTime, UNIX_EPOCH}; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; +use tasm_lib::twenty_first::util_types::mmr::mmr_trait::Mmr; use tokio::select; use tokio::sync::{mpsc, watch}; use tokio::task::JoinHandle; @@ -60,11 +61,13 @@ fn make_block_template( .apply(&mut next_mutator_set_accumulator) .expect("Mutator set mutation must work"); + let mut block_mmra = previous_block.kernel.body.block_mmr_accumulator.clone(); + block_mmra.append(previous_block.hash()); let block_body: BlockBody = BlockBody { transaction, mutator_set_accumulator: next_mutator_set_accumulator.clone(), lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), - block_mmr_accumulator: MmrAccumulator::::new(vec![]), + block_mmr_accumulator: block_mmra, uncle_blocks: vec![], }; diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index 10154304f..2a0185319 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -47,7 +47,8 @@ pub struct BlockBody { pub lock_free_mmr_accumulator: MmrAccumulator, /// All blocks live in an MMR, so that we can efficiently prove that a given block - /// lives on the line between the tip and genesis. + /// lives on the line between the tip and genesis. This MMRA does not contain the + /// current block. pub block_mmr_accumulator: MmrAccumulator, /// All blocks that lost the block race to an ancestor of this block and have not been diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index e69f8d54d..cb2a7f777 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use std::cmp::max; use tasm_lib::triton_vm::proof::Proof; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; +use tasm_lib::twenty_first::util_types::mmr::mmr_trait::Mmr; use twenty_first::shared_math::bfield_codec::BFieldCodec; use tracing::{debug, error, warn}; @@ -272,8 +273,8 @@ impl Block { // 0. `previous_block` is consistent with current block // a) Block height is previous plus one // b) Block header points to previous block - // c) Block timestamp is greater than previous block timestamp - // d) Target difficulty, and other control parameters, were adjusted correctly + // d) Block timestamp is greater than previous block timestamp + // e) Target difficulty, and other control parameters, were adjusted correctly // 1. The transaction is valid. // 1'. All transactions are valid. // a) verify that MS membership proof is valid, done against previous `mutator_set_accumulator`, @@ -297,7 +298,15 @@ impl Block { return false; } - // 0.c) Block timestamp is greater than that of previuos block + // 0.c) Verify correct addition to block MMR + let mut mmra = previous_block.kernel.body.block_mmr_accumulator.clone(); + mmra.append(previous_block.hash()); + if mmra != self.kernel.body.block_mmr_accumulator { + warn!("Block MMRA was not updated correctly"); + return false; + } + + // 0.d) Block timestamp is greater than that of previuos block if previous_block.kernel.header.timestamp.value() >= block_copy.kernel.header.timestamp.value() { @@ -305,7 +314,7 @@ impl Block { return false; } - // 0.d) Target difficulty, and other control parameters, were updated correctly + // 0.e) Target difficulty, and other control parameters, were updated correctly if block_copy.kernel.header.difficulty != Self::difficulty_control(previous_block, block_copy.kernel.header.timestamp.value()) { @@ -345,8 +354,8 @@ impl Block { return false; } - // 1.d) Verify that the two mutator sets, previous and current, are - // consistent with the transactions. + // 1.d) Verify that the two mutator sets, the one from the current block and the + // one from the previous, are consistent with the transactions. // Construct all the addition records for all the transaction outputs. Then // use these addition records to insert into the mutator set. let mutator_set_update = MutatorSetUpdate::new( @@ -502,7 +511,7 @@ mod block_tests { blockchain::transaction::PublicAnnouncement, state::wallet::WalletSecret, state::UtxoReceiverData, }, - tests::shared::{get_mock_global_state, make_mock_block}, + tests::shared::{get_mock_global_state, make_mock_block, make_mock_block_with_valid_pow}, }; use tasm_lib::twenty_first::util_types::emojihash_trait::Emojihash; @@ -611,4 +620,18 @@ mod block_tests { assert_eq!(some_threshold_actual, some_threshold_expected); assert_eq!(bfe_max_elem, some_threshold_actual.values()[3]); } + + #[test] + fn block_with_wrong_mmra_is_invalid() { + let genesis_block = Block::genesis_block(); + + let a_wallet_secret = WalletSecret::new_random(); + let a_recipient_address = a_wallet_secret.nth_generation_spending_key(0).to_address(); + let (mut block_1, _, _) = + make_mock_block_with_valid_pow(&genesis_block, None, a_recipient_address); + + block_1.kernel.body.block_mmr_accumulator = MmrAccumulator::new(vec![]); + + assert!(!block_1.is_valid(&genesis_block)); + } } diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 12a33865c..3fa5d97f1 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -907,6 +907,8 @@ pub fn make_mock_block( let mut next_mutator_set = previous_block.kernel.body.mutator_set_accumulator.clone(); let previous_mutator_set = next_mutator_set.clone(); + let mut block_mmr = previous_block.kernel.body.block_mmr_accumulator.clone(); + block_mmr.append(previous_block.hash()); let coinbase_digest: Digest = Hash::hash(&coinbase_utxo); let coinbase_addition_record: AdditionRecord = @@ -950,7 +952,7 @@ pub fn make_mock_block( transaction, mutator_set_accumulator: next_mutator_set.clone(), lock_free_mmr_accumulator: MmrAccumulator::::new(vec![]), - block_mmr_accumulator: MmrAccumulator::::new(vec![]), + block_mmr_accumulator: block_mmr, uncle_blocks: vec![], }; From f4bce56510611284517e7499b72ca2d06cbddc19 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 30 Jan 2024 17:22:14 +0100 Subject: [PATCH 23/29] add profiles and benchmarks for updated tasm programs --- ...m_neptune_transaction_compute_indices.json | 4 +- ...transaction_removal_records_integrity.json | 4 +- ...nsaction_transaction_kernel_mast_hash.json | 8 +- ...nsaction_removal_records_integrity.profile | 315 +++++++++--------- 4 files changed, 174 insertions(+), 157 deletions(-) diff --git a/benchmarks/tasm_neptune_transaction_compute_indices.json b/benchmarks/tasm_neptune_transaction_compute_indices.json index 61756f197..c341b6868 100644 --- a/benchmarks/tasm_neptune_transaction_compute_indices.json +++ b/benchmarks/tasm_neptune_transaction_compute_indices.json @@ -1,14 +1,14 @@ [ { "name": "tasm_neptune_transaction_compute_indices", - "clock_cycle_count": 5367, + "clock_cycle_count": 5375, "hash_table_height": 607, "u32_table_height": 4378, "case": "CommonCase" }, { "name": "tasm_neptune_transaction_compute_indices", - "clock_cycle_count": 5367, + "clock_cycle_count": 5375, "hash_table_height": 607, "u32_table_height": 4514, "case": "WorstCase" diff --git a/benchmarks/tasm_neptune_transaction_removal_records_integrity.json b/benchmarks/tasm_neptune_transaction_removal_records_integrity.json index 8ffde4dc8..fa9fe7729 100644 --- a/benchmarks/tasm_neptune_transaction_removal_records_integrity.json +++ b/benchmarks/tasm_neptune_transaction_removal_records_integrity.json @@ -1,8 +1,8 @@ [ { "name": "tasm_neptune_transaction_removal_records_integrity", - "clock_cycle_count": 29594, - "hash_table_height": 5621, + "clock_cycle_count": 29649, + "hash_table_height": 5633, "u32_table_height": 13245, "case": "CommonCase" } diff --git a/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json b/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json index 85b18e806..f6999219b 100644 --- a/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json +++ b/benchmarks/tasm_neptune_transaction_transaction_kernel_mast_hash.json @@ -1,15 +1,15 @@ [ { "name": "tasm_neptune_transaction_transaction_kernel_mast_hash", - "clock_cycle_count": 7754, - "hash_table_height": 3601, + "clock_cycle_count": 7758, + "hash_table_height": 3607, "u32_table_height": 73, "case": "CommonCase" }, { "name": "tasm_neptune_transaction_transaction_kernel_mast_hash", - "clock_cycle_count": 8486, - "hash_table_height": 3967, + "clock_cycle_count": 8490, + "hash_table_height": 3973, "u32_table_height": 74, "case": "WorstCase" } diff --git a/profiles/tasm_neptune_transaction_removal_records_integrity.profile b/profiles/tasm_neptune_transaction_removal_records_integrity.profile index 02b834559..0d826f3a8 100644 --- a/profiles/tasm_neptune_transaction_removal_records_integrity.profile +++ b/profiles/tasm_neptune_transaction_removal_records_integrity.profile @@ -1,9 +1,9 @@ tasm_neptune_transaction_removal_records_integrity: # call graph - tasm_neptune_transaction_transaction_kernel_mast_hash: 4658 - tasm_list_unsafeimplu32_new___digest: 36 - tasm_memory_dyn_malloc: 25 + tasm_neptune_transaction_transaction_kernel_mast_hash: 4662 + tasm_list_unsafeimplu32_new___digest: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___digest: 5 tasm_hashing_hash_varlen: 3114 tasm_hashing_absorb: 3100 @@ -124,18 +124,18 @@ tasm_neptune_transaction_removal_records_integrity: tasm_list_unsafeimplu32_get_element___digest: 8 tasm_list_unsafeimplu32_get_element___digest: 8 tasm_memory_push_ram_to_stack___digest: 5 - tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 113 + tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 117 tasm_list_contiguous_list_get_length: 3 - tasm_list_unsafeimplu32_new___void_pointer: 34 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___void_pointer: 38 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_contiguous_list_get_pointer_list_unsafeimplu32_loop: 55 tasm_list_unsafeimplu32_set_element___void_pointer: 6 tasm_list_unsafeimplu32_set_element___void_pointer: 6 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo: 449 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo: 453 tasm_list_unsafeimplu32_length___void_pointer: 3 - tasm_list_unsafeimplu32_new___digest: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___digest: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo_loop: 393 tasm_list_unsafeimplu32_get_element___void_pointer: 6 @@ -154,21 +154,21 @@ tasm_neptune_transaction_removal_records_integrity: tasm_hashing_absorb_pad_varnum_zeros: 5 tasm_hashing_absorb_read_remainder: 86 tasm_list_unsafeimplu32_set_element___digest: 8 - tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 113 + tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 117 tasm_list_contiguous_list_get_length: 3 - tasm_list_unsafeimplu32_new___void_pointer: 34 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___void_pointer: 38 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_contiguous_list_get_pointer_list_unsafeimplu32_loop: 55 tasm_list_unsafeimplu32_set_element___void_pointer: 6 tasm_list_unsafeimplu32_set_element___void_pointer: 6 - tasm_list_higher_order_unsafeimplu32_u32_zip_void_pointer_with_digest: 303 - tasm_list_unsafeimplu32_length___void_pointer: 3 + tasm_list_higher_order_unsafeimplu32_u32_zip_digest_with_void_pointer: 307 tasm_list_unsafeimplu32_length___digest: 3 - tasm_list_unsafeimplu32_new___tuple_Lvoid_pointer___digestR: 36 - tasm_memory_dyn_malloc: 25 - tasm_list_unsafeimplu32_set_length___tuple_Lvoid_pointer___digestR: 5 - tasm_list_higher_order_unsafeimplu32_u32_zip_void_pointer_with_digest_loop: 239 + tasm_list_unsafeimplu32_length___void_pointer: 3 + tasm_list_unsafeimplu32_new___tuple_Ldigest___void_pointerR: 40 + tasm_memory_dyn_malloc: 29 + tasm_list_unsafeimplu32_set_length___tuple_Ldigest___void_pointerR: 5 + tasm_list_higher_order_unsafeimplu32_u32_zip_digest_with_void_pointer_loop: 239 tasm_memory_memcpy: 38 tasm_memory_memcpy_loop_cpy5_words: 5 tasm_memory_memcpy_cpy1_word: 9 @@ -179,23 +179,23 @@ tasm_neptune_transaction_removal_records_integrity: tasm_memory_memcpy_cpy1_word: 9 tasm_memory_memcpy: 43 tasm_memory_memcpy_loop_cpy5_words: 20 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices: 10847 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices: 10867 tasm_list_unsafeimplu32_length___tuple_Ldigest___void_pointerR: 3 - tasm_list_unsafeimplu32_new___void_pointer: 34 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___void_pointer: 38 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___tuple_Ldigest___void_pointerR: 5 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices_loop: 10793 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices_loop: 10809 tasm_list_unsafeimplu32_get_element___tuple_Ldigest___void_pointerR: 9 - tasm_neptune_transaction_compute_indices: 5365 + tasm_neptune_transaction_compute_indices: 5373 tasm_memory_push_ram_to_stack___u64: 5 tasm_memory_push_ram_to_stack___digest: 5 tasm_memory_push_ram_to_stack___digest: 5 - tasm_neptune_mutator_get_swbf_indices_1048576_45: 5253 + tasm_neptune_mutator_get_swbf_indices_1048576_45: 5261 tasm_arithmetic_u128_shift_right_static_3: 24 tasm_arithmetic_u128_shift_left_static_12: 22 - sample_indices: 2778 - tasm_list_unsafeimplu32_new___u32: 34 - tasm_memory_dyn_malloc: 25 + sample_indices: 2782 + tasm_list_unsafeimplu32_new___u32: 38 + tasm_memory_dyn_malloc: 29 sample_indices_main_loop: 2734 tasm_list_unsafeimplu32_length___u32: 3 tasm_list_unsafeimplu32_length___u32: 3 @@ -348,10 +348,10 @@ tasm_neptune_transaction_removal_records_integrity: tasm_list_unsafeimplu32_length___u32: 3 sample_indices_else_drop_tip: 5 tasm_list_unsafeimplu32_length___u32: 3 - tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 2401 + tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 2405 tasm_list_unsafeimplu32_length___u32: 3 - tasm_list_unsafeimplu32_new___u128: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___u128: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___u32: 5 tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128_loop: 2345 tasm_list_unsafeimplu32_get_element___u32: 6 @@ -491,16 +491,16 @@ tasm_neptune_transaction_removal_records_integrity: tasm_list_unsafeimplu32_set_element___u128: 8 tasm_list_unsafeimplu32_set_element___void_pointer: 6 tasm_list_unsafeimplu32_get_element___tuple_Ldigest___void_pointerR: 9 - tasm_neptune_transaction_compute_indices: 5365 + tasm_neptune_transaction_compute_indices: 5373 tasm_memory_push_ram_to_stack___u64: 5 tasm_memory_push_ram_to_stack___digest: 5 tasm_memory_push_ram_to_stack___digest: 5 - tasm_neptune_mutator_get_swbf_indices_1048576_45: 5253 + tasm_neptune_mutator_get_swbf_indices_1048576_45: 5261 tasm_arithmetic_u128_shift_right_static_3: 24 tasm_arithmetic_u128_shift_left_static_12: 22 - sample_indices: 2778 - tasm_list_unsafeimplu32_new___u32: 34 - tasm_memory_dyn_malloc: 25 + sample_indices: 2782 + tasm_list_unsafeimplu32_new___u32: 38 + tasm_memory_dyn_malloc: 29 sample_indices_main_loop: 2734 tasm_list_unsafeimplu32_length___u32: 3 tasm_list_unsafeimplu32_length___u32: 3 @@ -653,10 +653,10 @@ tasm_neptune_transaction_removal_records_integrity: tasm_list_unsafeimplu32_length___u32: 3 sample_indices_else_drop_tip: 5 tasm_list_unsafeimplu32_length___u32: 3 - tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 2401 + tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 2405 tasm_list_unsafeimplu32_length___u32: 3 - tasm_list_unsafeimplu32_new___u128: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___u128: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___u32: 5 tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128_loop: 2345 tasm_list_unsafeimplu32_get_element___u32: 6 @@ -795,10 +795,10 @@ tasm_neptune_transaction_removal_records_integrity: u32_to_u128_add_another_u128: 24 tasm_list_unsafeimplu32_set_element___u128: 8 tasm_list_unsafeimplu32_set_element___void_pointer: 6 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list: 893 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list: 897 tasm_list_unsafeimplu32_length___void_pointer: 3 - tasm_list_unsafeimplu32_new___digest: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___digest: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list_loop: 837 tasm_list_unsafeimplu32_get_element___void_pointer: 6 @@ -817,18 +817,18 @@ tasm_neptune_transaction_removal_records_integrity: tasm_hashing_absorb_pad_varnum_zeros: 104 tasm_hashing_absorb_read_remainder: 5 tasm_list_unsafeimplu32_set_element___digest: 8 - tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 113 + tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 117 tasm_list_contiguous_list_get_length: 3 - tasm_list_unsafeimplu32_new___void_pointer: 34 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___void_pointer: 38 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_contiguous_list_get_pointer_list_unsafeimplu32_loop: 55 tasm_list_unsafeimplu32_set_element___void_pointer: 6 tasm_list_unsafeimplu32_set_element___void_pointer: 6 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices: 897 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices: 901 tasm_list_unsafeimplu32_length___void_pointer: 3 - tasm_list_unsafeimplu32_new___digest: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___digest: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___void_pointer: 5 tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices_loop: 841 tasm_list_unsafeimplu32_get_element___void_pointer: 6 @@ -865,10 +865,10 @@ tasm_neptune_transaction_removal_records_integrity: tasm_list_unsafeimplu32_multiset_equality_running_product_loop: 57 tasm_list_unsafeimplu32_multiset_equality_running_product: 71 tasm_list_unsafeimplu32_multiset_equality_running_product_loop: 57 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment: 311 + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment: 315 tasm_list_unsafeimplu32_length___tuple_Ldigest___void_pointerR: 3 - tasm_list_unsafeimplu32_new___tuple_Lvoid_pointer___digestR: 36 - tasm_memory_dyn_malloc: 25 + tasm_list_unsafeimplu32_new___tuple_Lvoid_pointer___digestR: 40 + tasm_memory_dyn_malloc: 29 tasm_list_unsafeimplu32_set_length___tuple_Ldigest___void_pointerR: 5 tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment_loop: 255 tasm_list_unsafeimplu32_get_element___tuple_Ldigest___void_pointerR: 9 @@ -1486,104 +1486,121 @@ tasm_neptune_transaction_removal_records_integrity: tasm_arithmetic_u64_eq: 6 tasm_list_unsafeimplu32_get_element___digest: 8 tasm_hashing_eq_digest: 15 - total: 29594 + total: 29649 # aggregated - tasm_neptune_transaction_transaction_kernel_mast_hash: 4658 - tasm_list_unsafeimplu32_new___digest: 144 - tasm_memory_dyn_malloc: 350 - tasm_list_unsafeimplu32_set_length___digest: 5 - tasm_hashing_hash_varlen: 6314 - tasm_hashing_absorb: 6104 - tasm_hashing_absorb_hash_all_full_chunks: 3999 - tasm_hashing_absorb_pad_varnum_zeros: 1175 - tasm_hashing_absorb_read_remainder: 390 - tasm_list_unsafeimplu32_set_element___digest: 168 - tasm_list_unsafeimplu32_get_element___digest: 1480 - tasm_memory_push_ram_to_stack___digest: 50 - tasm_mmr_bag_peaks: 930 - tasm_list_unsafeimplu32_length___digest: 15 - tasm_mmr_bag_peaks_length_is_not_zero: 904 - tasm_mmr_bag_peaks_length_is_not_zero_or_one: 888 - tasm_mmr_bag_peaks_loop: 808 - tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 339 - tasm_list_contiguous_list_get_length: 9 - tasm_list_unsafeimplu32_new___void_pointer: 136 - tasm_list_unsafeimplu32_set_length___void_pointer: 30 - tasm_list_contiguous_list_get_pointer_list_unsafeimplu32_loop: 165 - tasm_list_unsafeimplu32_set_element___void_pointer: 48 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo: 449 - tasm_list_unsafeimplu32_length___void_pointer: 12 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo_loop: 393 - tasm_list_unsafeimplu32_get_element___void_pointer: 36 - tasm_neptune_transaction_hash_utxo: 332 - tasm_list_higher_order_unsafeimplu32_u32_zip_void_pointer_with_digest: 303 - tasm_list_unsafeimplu32_new___tuple_Lvoid_pointer___digestR: 72 - tasm_list_unsafeimplu32_set_length___tuple_Lvoid_pointer___digestR: 5 - tasm_list_higher_order_unsafeimplu32_u32_zip_void_pointer_with_digest_loop: 239 - tasm_memory_memcpy: 162 - tasm_memory_memcpy_loop_cpy5_words: 50 - tasm_memory_memcpy_cpy1_word: 18 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices: 10847 - tasm_list_unsafeimplu32_length___tuple_Ldigest___void_pointerR: 6 - tasm_list_unsafeimplu32_set_length___tuple_Ldigest___void_pointerR: 10 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices_loop: 10793 - tasm_list_unsafeimplu32_get_element___tuple_Ldigest___void_pointerR: 36 - tasm_neptune_transaction_compute_indices: 10730 - tasm_memory_push_ram_to_stack___u64: 10 - tasm_neptune_mutator_get_swbf_indices_1048576_45: 10506 - tasm_arithmetic_u128_shift_right_static_3: 48 - tasm_arithmetic_u128_shift_left_static_12: 44 - sample_indices: 5556 - tasm_list_unsafeimplu32_new___u32: 68 - sample_indices_main_loop: 5468 - tasm_list_unsafeimplu32_length___u32: 342 - sample_indices_then_reduce_and_save: 3060 - tasm_list_unsafeimplu32_push___u32: 1620 - sample_indices_else_drop_tip: 50 - tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 4802 - tasm_list_unsafeimplu32_new___u128: 72 - tasm_list_unsafeimplu32_set_length___u32: 10 - tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128_loop: 4690 - tasm_list_unsafeimplu32_get_element___u32: 540 - u32_to_u128_add_another_u128: 2160 - tasm_list_unsafeimplu32_set_element___u128: 720 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list: 893 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list_loop: 837 - tasm_neptune_transaction_hash_index_list: 776 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices: 897 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices_loop: 841 - tasm_neptune_transaction_hash_removal_record_indices: 780 - tasm_list_unsafeimplu32_multiset_equality: 553 - tasm_list_unsafeimplu32_multiset_equality_continue: 532 - tasm_list_unsafeimplu32_multiset_equality_running_product: 142 - tasm_list_unsafeimplu32_multiset_equality_running_product_loop: 114 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment: 311 - tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment_loop: 255 - tasm_neptune_transaction_compute_commitment: 186 - tasm_neptune_mutator_set_commit: 6 - tasm_list_unsafeimplu32_set_element___tuple_Lvoid_pointer___digestR: 18 - tasm_list_higher_order_unsafeimplu32_u32_all_tasm_neptune_transaction_verify_aocl_membership: 9111 - tasm_list_unsafeimplu32_length___tuple_Lvoid_pointer___digestR: 3 - tasm_list_higher_order_unsafeimplu32_u32_all_tasm_neptune_transaction_verify_aocl_membership_loop: 9101 - tasm_list_unsafeimplu32_get_element___tuple_Lvoid_pointer___digestR: 18 - tasm_neptune_transaction_verify_aocl_membership: 9048 - tasm_mmr_verify_from_memory_unsafeimplu32: 8902 - tasm_mmr_leaf_index_to_mt_index_and_peak_index: 248 - tasm_arithmetic_u64_lt: 12 - tasm_arithmetic_u64_xor: 10 - tasm_arithmetic_u64_log_2_floor: 30 - tasm_arithmetic_u64_log_2_floor_then: 16 - tasm_arithmetic_u64_pow2: 8 - tasm_arithmetic_u64_decr: 38 - tasm_arithmetic_u64_decr_carry: 22 - tasm_arithmetic_u64_and: 20 - tasm_arithmetic_u64_add: 28 - tasm_arithmetic_u64_popcount: 20 - tasm_mmr_verify_from_memory_unsafeimplu32_while: 8552 - tasm_arithmetic_u64_eq: 768 - tasm_arithmetic_u32_isodd: 756 - tasm_arithmetic_u64_div2: 1764 - tasm_hashing_swap_digest: 784 - tasm_hashing_eq_digest: 30 - total: 29594 + tasm_neptune_transaction_transaction_kernel_mast_hash: 4662 (1) + tasm_list_unsafeimplu32_new___digest: 160 (0.034320034) + tasm_memory_dyn_malloc: 290 (0.06220506) + tasm_list_unsafeimplu32_set_length___digest: 5 (0.0010725011) + tasm_hashing_hash_varlen: 4126 (0.8850279) + tasm_hashing_absorb: 4028 (0.8640069) + tasm_hashing_absorb_hash_all_full_chunks: 3047 (0.65358216) + tasm_hashing_absorb_pad_varnum_zeros: 541 (0.11604462) + tasm_hashing_absorb_read_remainder: 188 (0.04032604) + tasm_list_unsafeimplu32_set_element___digest: 120 (0.025740026) + tasm_list_unsafeimplu32_get_element___digest: 120 (0.025740026) + tasm_memory_push_ram_to_stack___digest: 10 (0.0021450021) + tasm_mmr_bag_peaks: 930 (0.1994852) + tasm_list_unsafeimplu32_length___digest: 15 (0.0032175032) + tasm_mmr_bag_peaks_length_is_not_zero: 904 (0.1939082) + tasm_mmr_bag_peaks_length_is_not_zero_or_one: 888 (0.1904762) + tasm_list_unsafeimplu32_get_element___digest: 32 (0.006864007) + tasm_mmr_bag_peaks_loop: 808 (0.17331618) + tasm_list_unsafeimplu32_get_element___digest: 320 (0.06864007) + tasm_list_contiguous_list_get_pointer_list_unsafeimplu32: 351 (0.07528958) + tasm_list_contiguous_list_get_length: 9 (0.0019305019) + tasm_list_unsafeimplu32_new___void_pointer: 152 (0.03260403) + tasm_list_unsafeimplu32_set_length___void_pointer: 30 (0.0064350064) + tasm_list_contiguous_list_get_pointer_list_unsafeimplu32_loop: 165 (0.035392534) + tasm_list_unsafeimplu32_set_element___void_pointer: 48 (0.01029601) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo: 453 (0.097168595) + tasm_list_unsafeimplu32_length___void_pointer: 12 (0.0025740026) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_utxo_loop: 393 (0.08429858) + tasm_list_unsafeimplu32_get_element___void_pointer: 36 (0.0077220076) + tasm_neptune_transaction_hash_utxo: 332 (0.07121407) + tasm_hashing_hash_varlen: 1836 (0.3938224) + tasm_hashing_absorb: 1752 (0.37580436) + tasm_hashing_absorb_hash_all_full_chunks: 918 (0.1969112) + tasm_hashing_absorb_pad_varnum_zeros: 426 (0.091377094) + tasm_hashing_absorb_read_remainder: 192 (0.04118404) + tasm_list_unsafeimplu32_set_element___digest: 48 (0.01029601) + tasm_list_higher_order_unsafeimplu32_u32_zip_digest_with_void_pointer: 307 (0.06585157) + tasm_list_unsafeimplu32_new___tuple_Ldigest___void_pointerR: 40 (0.0085800085) + tasm_list_unsafeimplu32_set_length___tuple_Ldigest___void_pointerR: 15 (0.0032175032) + tasm_list_higher_order_unsafeimplu32_u32_zip_digest_with_void_pointer_loop: 239 (0.051265553) + tasm_memory_memcpy: 162 (0.034749035) + tasm_memory_memcpy_loop_cpy5_words: 50 (0.010725011) + tasm_memory_memcpy_cpy1_word: 18 (0.0038610038) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices: 10867 (2.3309739) + tasm_list_unsafeimplu32_length___tuple_Ldigest___void_pointerR: 6 (0.0012870013) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_indices_loop: 10809 (2.3185327) + tasm_list_unsafeimplu32_get_element___tuple_Ldigest___void_pointerR: 36 (0.0077220076) + tasm_neptune_transaction_compute_indices: 10746 (2.3050194) + tasm_memory_push_ram_to_stack___u64: 10 (0.0021450021) + tasm_memory_push_ram_to_stack___digest: 40 (0.0085800085) + tasm_neptune_mutator_get_swbf_indices_1048576_45: 10522 (2.2569714) + tasm_arithmetic_u128_shift_right_static_3: 48 (0.01029601) + tasm_arithmetic_u128_shift_left_static_12: 44 (0.009438009) + sample_indices: 5564 (1.1934792) + tasm_list_unsafeimplu32_new___u32: 76 (0.016302016) + tasm_memory_dyn_malloc: 116 (0.024882024) + sample_indices_main_loop: 5468 (1.1728872) + tasm_list_unsafeimplu32_length___u32: 336 (0.072072074) + sample_indices_then_reduce_and_save: 3060 (0.65637064) + tasm_list_unsafeimplu32_push___u32: 1620 (0.34749034) + sample_indices_else_drop_tip: 50 (0.010725011) + tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128: 4810 (1.031746) + tasm_list_unsafeimplu32_length___u32: 6 (0.0012870013) + tasm_list_unsafeimplu32_new___u128: 80 (0.017160017) + tasm_list_unsafeimplu32_set_length___u32: 10 (0.0021450021) + tasm_list_higher_order_unsafeimplu32_u32_map_u32_to_u128_add_another_u128_loop: 4690 (1.006006) + tasm_list_unsafeimplu32_get_element___u32: 540 (0.115830116) + u32_to_u128_add_another_u128: 2160 (0.46332046) + tasm_list_unsafeimplu32_set_element___u128: 720 (0.15444015) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list: 897 (0.1924067) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_index_list_loop: 837 (0.17953669) + tasm_neptune_transaction_hash_index_list: 776 (0.16645217) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices: 901 (0.1932647) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_hash_removal_record_indices_loop: 841 (0.18039468) + tasm_neptune_transaction_hash_removal_record_indices: 780 (0.16731016) + tasm_list_unsafeimplu32_multiset_equality: 553 (0.118618615) + tasm_list_unsafeimplu32_multiset_equality_continue: 532 (0.11411411) + tasm_hashing_hash_varlen: 352 (0.07550407) + tasm_hashing_absorb: 324 (0.06949807) + tasm_hashing_absorb_hash_all_full_chunks: 34 (0.0072930073) + tasm_hashing_absorb_pad_varnum_zeros: 208 (0.044616044) + tasm_hashing_absorb_read_remainder: 10 (0.0021450021) + tasm_list_unsafeimplu32_multiset_equality_running_product: 142 (0.03045903) + tasm_list_unsafeimplu32_multiset_equality_running_product_loop: 114 (0.024453025) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment: 315 (0.067567565) + tasm_list_unsafeimplu32_new___tuple_Lvoid_pointer___digestR: 40 (0.0085800085) + tasm_list_higher_order_unsafeimplu32_u32_map_tasm_neptune_transaction_compute_commitment_loop: 255 (0.054697555) + tasm_neptune_transaction_compute_commitment: 186 (0.03989704) + tasm_neptune_mutator_set_commit: 6 (0.0012870013) + tasm_list_unsafeimplu32_set_element___tuple_Lvoid_pointer___digestR: 18 (0.0038610038) + tasm_list_higher_order_unsafeimplu32_u32_all_tasm_neptune_transaction_verify_aocl_membership: 9111 (1.9543115) + tasm_list_unsafeimplu32_length___tuple_Lvoid_pointer___digestR: 3 (0.00064350065) + tasm_list_higher_order_unsafeimplu32_u32_all_tasm_neptune_transaction_verify_aocl_membership_loop: 9101 (1.9521664) + tasm_list_unsafeimplu32_get_element___tuple_Lvoid_pointer___digestR: 18 (0.0038610038) + tasm_neptune_transaction_verify_aocl_membership: 9048 (1.9407979) + tasm_mmr_verify_from_memory_unsafeimplu32: 8902 (1.9094809) + tasm_mmr_leaf_index_to_mt_index_and_peak_index: 248 (0.053196054) + tasm_arithmetic_u64_lt: 12 (0.0025740026) + tasm_arithmetic_u64_xor: 10 (0.0021450021) + tasm_arithmetic_u64_log_2_floor: 30 (0.0064350064) + tasm_arithmetic_u64_log_2_floor_then: 16 (0.0034320035) + tasm_arithmetic_u64_pow2: 8 (0.0017160018) + tasm_arithmetic_u64_decr: 38 (0.008151008) + tasm_arithmetic_u64_decr_carry: 22 (0.0047190045) + tasm_arithmetic_u64_and: 20 (0.0042900043) + tasm_arithmetic_u64_add: 28 (0.006006006) + tasm_arithmetic_u64_popcount: 20 (0.0042900043) + tasm_mmr_verify_from_memory_unsafeimplu32_while: 8552 (1.8344058) + tasm_arithmetic_u64_eq: 768 (0.16473617) + tasm_list_unsafeimplu32_get_element___digest: 1008 (0.21621622) + tasm_arithmetic_u32_isodd: 756 (0.16216215) + tasm_arithmetic_u64_div2: 1764 (0.3783784) + tasm_hashing_swap_digest: 784 (0.16816817) + tasm_hashing_eq_digest: 30 (0.0064350064) + total: 29649 (6.359717) From 648412682d9094cfd778aeeff92495b85d78b7d0 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Thu, 1 Feb 2024 15:45:05 +0100 Subject: [PATCH 24/29] add profiler for mutator set accumulator size --- .../mutator_set/mutator_set_accumulator.rs | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/src/util_types/mutator_set/mutator_set_accumulator.rs b/src/util_types/mutator_set/mutator_set_accumulator.rs index 798b05d73..6df84a728 100644 --- a/src/util_types/mutator_set/mutator_set_accumulator.rs +++ b/src/util_types/mutator_set/mutator_set_accumulator.rs @@ -99,9 +99,12 @@ impl MutatorSet for MutatorSetAccumulator)> = vec![]; + let target_set_size = 100; + let num_iterations = 10000; + + for i in 0..num_iterations { + if i % 100 == 0 { + println!("{}/{}", i, num_iterations); + } + let operation = if items_and_membership_proofs.len() + > (1.25 * target_set_size as f64) as usize + { + rng.gen_range(0..10) >= 3 + } else if items_and_membership_proofs.len() < (0.8 * target_set_size as f64) as usize { + rng.gen_range(0..10) < 3 + } else { + rng.gen_range(0..10) < 5 + }; + if operation && !items_and_membership_proofs.is_empty() { + // removal + let index = rng.gen_range(0..items_and_membership_proofs.len()); + let (item, membership_proof) = items_and_membership_proofs.swap_remove(index); + let removal_record = msa.drop(item, &membership_proof); + for (_it, mp) in items_and_membership_proofs.iter_mut() { + mp.update_from_remove(&removal_record).unwrap(); + } + msa.remove(&removal_record); + } else { + // addition + let item = rng.gen::(); + let sender_randomness = rng.gen::(); + let receiver_preimage = rng.gen::(); + let addition_record = commit::(item, sender_randomness, receiver_preimage); + for (it, mp) in items_and_membership_proofs.iter_mut() { + mp.update_from_addition(*it, &msa, &addition_record) + .unwrap(); + } + let membership_proof = msa.prove(item, sender_randomness, receiver_preimage); + msa.add(&addition_record); + items_and_membership_proofs.push((item, membership_proof)); + } + } + + println!("{} operations resulted in a set containin {} elements; mutator set accumulator size: {} bytes", num_iterations, items_and_membership_proofs.len(), msa.get_size()); + } } From bc4e3c14aa0a5aa5ac96d2881ad21f3360394868 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Thu, 1 Feb 2024 15:54:04 +0100 Subject: [PATCH 25/29] refactor(block): Drop body hash from header The block header does not need to commit to the block body. It is a potential source of discrepancy and at best surplus weight. Note that the block header does not commit to the body. However, the proof-of-work puzzle is defined in terms of the *block's hash*, which is `H(H(header)||H(body))`, so the *pair* is committed to when the miner expends energy. --- src/mine_loop.rs | 1 - src/models/blockchain/block/block_header.rs | 5 ----- src/models/blockchain/block/mod.rs | 8 -------- src/tests/shared.rs | 1 - 4 files changed, 15 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index 0b739e3fb..b335d6650 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -97,7 +97,6 @@ fn make_block_template( proof_of_work_line: new_pow_line, proof_of_work_family: new_pow_line, difficulty, - block_body_merkle_root: Hash::hash(&block_body), }; (block_header, block_body) diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 4367ec867..9b27e58e3 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -41,7 +41,6 @@ pub struct BlockHeader { // This is the difficulty for the *next* block. Unit: expected # hashes pub difficulty: U32s, - pub block_body_merkle_root: Digest, } impl Display for BlockHeader { @@ -74,7 +73,6 @@ pub enum BlockHeaderField { ProofOfWorkLine, ProofOfWorkFamily, Difficulty, - BlockBodyMerkleRoot, } impl HasDiscriminant for BlockHeaderField { @@ -90,7 +88,6 @@ impl HasDiscriminant for BlockHeaderField { BlockHeaderField::ProofOfWorkLine => 7, BlockHeaderField::ProofOfWorkFamily => 8, BlockHeaderField::Difficulty => 9, - BlockHeaderField::BlockBodyMerkleRoot => 10, } } } @@ -110,7 +107,6 @@ impl MastHash for BlockHeader { self.proof_of_work_line.encode(), self.proof_of_work_family.encode(), self.difficulty.encode(), - self.block_body_merkle_root.encode(), ] } } @@ -134,7 +130,6 @@ mod block_header_tests { proof_of_work_line: rng.gen(), proof_of_work_family: rng.gen(), difficulty: rng.gen(), - block_body_merkle_root: rng.gen(), } } #[test] diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index cb2a7f777..cda374699 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -187,7 +187,6 @@ impl Block { proof_of_work_line: U32s::zero(), proof_of_work_family: U32s::zero(), difficulty: MINIMUM_DIFFICULTY.into(), - block_body_merkle_root: Hash::hash(&body), }; Self::new(header, body, None) @@ -253,7 +252,6 @@ impl Block { proof_of_work_line: self.kernel.header.proof_of_work_line, proof_of_work_family: self.kernel.header.proof_of_work_family, difficulty: self.kernel.header.difficulty, - block_body_merkle_root: Hash::hash(&block_body), }; self.kernel.body = block_body; @@ -430,12 +428,6 @@ impl Block { // 4.1. verify that uncle's prev_block_digest matches with parent's prev_block_digest // 4.2. verify that all uncles' hash are below parent's target_difficulty - // 5. `block_body_merkle_root` - if block_copy.kernel.header.block_body_merkle_root != Hash::hash(&block_copy.kernel.body) { - warn!("Block body does not match referenced block body Merkle root"); - return false; - } - true } diff --git a/src/tests/shared.rs b/src/tests/shared.rs index 3fa5d97f1..a092c34dd 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -972,7 +972,6 @@ pub fn make_mock_block( proof_of_work_line: pow_family, proof_of_work_family: pow_family, difficulty: target_difficulty, - block_body_merkle_root: Hash::hash(&block_body), }; ( From 7821efe17f05bb4962aa55fe312b65c9acd0ba47 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Thu, 1 Feb 2024 16:04:20 +0100 Subject: [PATCH 26/29] refactor(block): Drop mutator set hash The mutator set is already present in the block. Keeping its hash in the header constitutes duplicate representation of data and is a source of potential discrepancy. Moreover, it was not used anywhere, as far as I can tell. --- src/mine_loop.rs | 2 -- src/models/blockchain/block/block_header.rs | 19 +++++++------------ src/models/blockchain/block/mod.rs | 8 -------- src/tests/shared.rs | 1 - 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/mine_loop.rs b/src/mine_loop.rs index b335d6650..dff7d8d0b 100644 --- a/src/mine_loop.rs +++ b/src/mine_loop.rs @@ -74,7 +74,6 @@ fn make_block_template( let zero = BFieldElement::zero(); let new_pow_line: U32s<5> = previous_block.kernel.header.proof_of_work_family + previous_block.kernel.header.difficulty; - let mutator_set_commitment: Digest = next_mutator_set_accumulator.hash(); let next_block_height = previous_block.kernel.header.height.next(); let mut block_timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -89,7 +88,6 @@ fn make_block_template( let block_header = BlockHeader { version: zero, height: next_block_height, - mutator_set_hash: mutator_set_commitment, prev_block_digest: previous_block.kernel.mast_hash(), timestamp: BFieldElement::new(block_timestamp), nonce: [zero, zero, zero], diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 9b27e58e3..49e374138 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -22,7 +22,6 @@ pub const MINIMUM_DIFFICULTY: u32 = 2; pub struct BlockHeader { pub version: BFieldElement, pub height: BlockHeight, - pub mutator_set_hash: Digest, pub prev_block_digest: Digest, // TODO: Reject blocks that are more than 10 seconds into the future @@ -65,7 +64,6 @@ impl Display for BlockHeader { pub enum BlockHeaderField { Version, Height, - MutatorSetHash, PrevBlockDigest, Timestamp, Nonce, @@ -80,14 +78,13 @@ impl HasDiscriminant for BlockHeaderField { match self { BlockHeaderField::Version => 0, BlockHeaderField::Height => 1, - BlockHeaderField::MutatorSetHash => 2, - BlockHeaderField::PrevBlockDigest => 3, - BlockHeaderField::Timestamp => 4, - BlockHeaderField::Nonce => 5, - BlockHeaderField::MaxBlockSize => 6, - BlockHeaderField::ProofOfWorkLine => 7, - BlockHeaderField::ProofOfWorkFamily => 8, - BlockHeaderField::Difficulty => 9, + BlockHeaderField::PrevBlockDigest => 2, + BlockHeaderField::Timestamp => 3, + BlockHeaderField::Nonce => 4, + BlockHeaderField::MaxBlockSize => 5, + BlockHeaderField::ProofOfWorkLine => 6, + BlockHeaderField::ProofOfWorkFamily => 7, + BlockHeaderField::Difficulty => 8, } } } @@ -99,7 +96,6 @@ impl MastHash for BlockHeader { vec![ self.version.encode(), self.height.encode(), - self.mutator_set_hash.encode(), self.prev_block_digest.encode(), self.timestamp.encode(), self.nonce.encode(), @@ -122,7 +118,6 @@ mod block_header_tests { BlockHeader { version: rng.gen(), height: BlockHeight::from(rng.gen::()), - mutator_set_hash: rng.gen(), prev_block_digest: rng.gen(), timestamp: rng.gen(), nonce: rng.gen(), diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index cda374699..ca58a0868 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -175,7 +175,6 @@ impl Block { let header: BlockHeader = BlockHeader { version: BFieldElement::zero(), height: BFieldElement::zero().into(), - mutator_set_hash: genesis_mutator_set.hash(), prev_block_digest: Digest::default(), timestamp, nonce: [ @@ -244,7 +243,6 @@ impl Block { let block_header = BlockHeader { version: self.kernel.header.version, height: self.kernel.header.height, - mutator_set_hash: next_mutator_set_accumulator.hash(), prev_block_digest: self.kernel.header.prev_block_digest, timestamp: merged_timestamp, nonce: self.kernel.header.nonce, @@ -381,12 +379,6 @@ impl Block { return false; } - // Verify that the locally constructed mutator set matches that in the received block's header - if ms.hash() != block_copy.kernel.header.mutator_set_hash { - warn!("Mutator set commitment does not match calculated object"); - return false; - } - // 1.e) verify that the transaction timestamp is less than or equal to the block's timestamp. if block_copy.kernel.body.transaction.kernel.timestamp.value() > block_copy.kernel.header.timestamp.value() diff --git a/src/tests/shared.rs b/src/tests/shared.rs index a092c34dd..33b7bb50d 100644 --- a/src/tests/shared.rs +++ b/src/tests/shared.rs @@ -964,7 +964,6 @@ pub fn make_mock_block( let block_header = BlockHeader { version: zero, height: new_block_height, - mutator_set_hash: next_mutator_set.hash(), prev_block_digest: previous_block.hash(), timestamp: block_body.transaction.kernel.timestamp, nonce: [zero, zero, zero], From e8b858bb0b344bccbf8b6c5498b790fd4499bfb6 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Thu, 1 Feb 2024 16:20:11 +0100 Subject: [PATCH 27/29] docs: Clarify validity of uncle blocks --- src/models/blockchain/block/block_body.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index 2a0185319..b16918f1b 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -52,7 +52,8 @@ pub struct BlockBody { pub block_mmr_accumulator: MmrAccumulator, /// All blocks that lost the block race to an ancestor of this block and have not been - /// listed as uncle before. + /// listed as uncle before. The miner will need to prove that between his block and + /// its least common ancestor with the uncle block, it was not listed. pub uncle_blocks: Vec, } From 696facde0bfbbd84472dde565da6867360bf92de Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Fri, 2 Feb 2024 10:01:27 +0100 Subject: [PATCH 28/29] test(block mmr): Can prove block ancestry --- src/models/blockchain/block/mod.rs | 70 +++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index ca58a0868..283d4e772 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -497,11 +497,18 @@ mod block_tests { }, tests::shared::{get_mock_global_state, make_mock_block, make_mock_block_with_valid_pow}, }; - use tasm_lib::twenty_first::util_types::emojihash_trait::Emojihash; + use tasm_lib::twenty_first::{ + storage::level_db::DB, + util_types::{ + emojihash_trait::Emojihash, + mmr::archival_mmr::ArchivalMmr, + storage_schema::{SimpleRustyStorage, StorageWriter}, + }, + }; use super::*; - use rand::random; + use rand::{random, thread_rng, Rng}; use tracing_test::traced_test; #[traced_test] @@ -618,4 +625,63 @@ mod block_tests { assert!(!block_1.is_valid(&genesis_block)); } + + #[test] + fn can_prove_block_ancestry() { + let genesis_block = Block::genesis_block(); + let mut blocks = vec![]; + blocks.push(genesis_block.clone()); + let db = DB::open_new_test_database(true, None, None, None).unwrap(); + let mut storage = SimpleRustyStorage::new(db); + storage.restore_or_new(); + let ammr_storage = storage.schema.new_vec::("ammr-blocks-0"); + let mut ammr: ArchivalMmr = ArchivalMmr::new(ammr_storage); + ammr.append(genesis_block.hash()); + let mut mmra = MmrAccumulator::new(vec![genesis_block.hash()]); + + for i in 0..55 { + let wallet_secret = WalletSecret::new_random(); + let recipient_address = wallet_secret.nth_generation_spending_key(0).to_address(); + let (new_block, _, _) = + make_mock_block(blocks.last().unwrap(), None, recipient_address); + if i != 54 { + ammr.append(new_block.hash()); + mmra.append(new_block.hash()); + assert_eq!(ammr.to_accumulator().bag_peaks(), mmra.bag_peaks()); + } + blocks.push(new_block); + } + + let last_block_mmra = blocks.last().unwrap().body().block_mmr_accumulator.clone(); + assert_eq!(mmra, last_block_mmra); + + let index = thread_rng().gen_range(0..blocks.len() - 1); + let block_digest = blocks[index].hash(); + let (membership_proof, _) = ammr.prove_membership(index as u64); + let (v, _) = membership_proof.verify( + &last_block_mmra.get_peaks(), + block_digest, + last_block_mmra.count_leaves(), + ); + assert!( + v, + "peaks: {} ({}) leaf count: {} index: {} path: {} number of blocks: {} leaf index: {}", + last_block_mmra + .get_peaks() + .iter() + .map(|d| d.emojihash()) + .join(","), + last_block_mmra.get_peaks().len(), + last_block_mmra.count_leaves(), + membership_proof.leaf_index, + membership_proof + .authentication_path + .iter() + .map(|d| d.emojihash()) + .join(","), + blocks.len(), + membership_proof.leaf_index + ); + assert_eq!(last_block_mmra.count_leaves(), blocks.len() as u64 - 1); + } } From 78b9793af690b6b538dcf6e3a6953ed96b6c22bb Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Fri, 2 Feb 2024 11:18:27 +0100 Subject: [PATCH 29/29] refactor: Simplify `HasDiscriminant` trait For enums without associated data (as used here), casting to usize gives the discriminant. This commit switches to that as it is less prone to error, but keeps the `.discriminant()` interface. --- src/models/blockchain/block/block_body.rs | 10 ++--- src/models/blockchain/block/block_header.rs | 13 +----- src/models/blockchain/block/block_kernel.rs | 6 +-- .../transaction/transaction_kernel.rs | 11 +---- src/models/consensus/mast_hash.rs | 45 ++++++++++++++++++- 5 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/models/blockchain/block/block_body.rs b/src/models/blockchain/block/block_body.rs index b16918f1b..a03010e6d 100644 --- a/src/models/blockchain/block/block_body.rs +++ b/src/models/blockchain/block/block_body.rs @@ -3,6 +3,7 @@ use crate::prelude::twenty_first; use get_size::GetSize; use serde::{Deserialize, Serialize}; +use strum::EnumCount; use tasm_lib::twenty_first::shared_math::b_field_element::BFieldElement; use tasm_lib::twenty_first::util_types::mmr::mmr_accumulator::MmrAccumulator; use tasm_lib::Digest; @@ -12,6 +13,7 @@ use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::Transaction; use crate::util_types::mutator_set::mutator_set_accumulator::MutatorSetAccumulator; +#[derive(Debug, Clone, EnumCount)] pub enum BlockBodyField { Transaction, MutatorSetAccumulator, @@ -22,13 +24,7 @@ pub enum BlockBodyField { impl HasDiscriminant for BlockBodyField { fn discriminant(&self) -> usize { - match self { - BlockBodyField::Transaction => 0, - BlockBodyField::MutatorSetAccumulator => 1, - BlockBodyField::LockFreeMmrAccumulator => 2, - BlockBodyField::BlockMmrAccumulator => 3, - BlockBodyField::UncleBlocks => 4, - } + self.clone() as usize } } diff --git a/src/models/blockchain/block/block_header.rs b/src/models/blockchain/block/block_header.rs index 49e374138..d766277b3 100644 --- a/src/models/blockchain/block/block_header.rs +++ b/src/models/blockchain/block/block_header.rs @@ -61,6 +61,7 @@ impl Display for BlockHeader { } } +#[derive(Debug, Clone)] pub enum BlockHeaderField { Version, Height, @@ -75,17 +76,7 @@ pub enum BlockHeaderField { impl HasDiscriminant for BlockHeaderField { fn discriminant(&self) -> usize { - match self { - BlockHeaderField::Version => 0, - BlockHeaderField::Height => 1, - BlockHeaderField::PrevBlockDigest => 2, - BlockHeaderField::Timestamp => 3, - BlockHeaderField::Nonce => 4, - BlockHeaderField::MaxBlockSize => 5, - BlockHeaderField::ProofOfWorkLine => 6, - BlockHeaderField::ProofOfWorkFamily => 7, - BlockHeaderField::Difficulty => 8, - } + self.clone() as usize } } diff --git a/src/models/blockchain/block/block_kernel.rs b/src/models/blockchain/block/block_kernel.rs index 57ce60a57..24dd8f3ea 100644 --- a/src/models/blockchain/block/block_kernel.rs +++ b/src/models/blockchain/block/block_kernel.rs @@ -13,6 +13,7 @@ pub struct BlockKernel { pub body: BlockBody, } +#[derive(Debug, Clone)] pub enum BlockKernelField { Header, Body, @@ -20,10 +21,7 @@ pub enum BlockKernelField { impl HasDiscriminant for BlockKernelField { fn discriminant(&self) -> usize { - match self { - BlockKernelField::Header => 0, - BlockKernelField::Body => 1, - } + self.clone() as usize } } diff --git a/src/models/blockchain/transaction/transaction_kernel.rs b/src/models/blockchain/transaction/transaction_kernel.rs index c1c8b4feb..8922530a1 100644 --- a/src/models/blockchain/transaction/transaction_kernel.rs +++ b/src/models/blockchain/transaction/transaction_kernel.rs @@ -45,6 +45,7 @@ pub struct TransactionKernel { pub mutator_set_hash: Digest, } +#[derive(Debug, Clone)] pub enum TransactionKernelField { InputUtxos, OutputUtxos, @@ -57,15 +58,7 @@ pub enum TransactionKernelField { impl HasDiscriminant for TransactionKernelField { fn discriminant(&self) -> usize { - match self { - TransactionKernelField::InputUtxos => 0, - TransactionKernelField::OutputUtxos => 1, - TransactionKernelField::Pubscript => 2, - TransactionKernelField::Fee => 3, - TransactionKernelField::Coinbase => 4, - TransactionKernelField::Timestamp => 5, - TransactionKernelField::MutatorSetHash => 6, - } + self.clone() as usize } } diff --git a/src/models/consensus/mast_hash.rs b/src/models/consensus/mast_hash.rs index ba1c29cb8..604764fce 100644 --- a/src/models/consensus/mast_hash.rs +++ b/src/models/consensus/mast_hash.rs @@ -10,8 +10,11 @@ use tasm_lib::twenty_first::{ use crate::models::blockchain::shared::Hash; -pub trait HasDiscriminant { +pub trait HasDiscriminant: Clone { fn discriminant(&self) -> usize; + // { + // self.clone() as usize + // } } pub trait MastHash { @@ -44,3 +47,43 @@ pub trait MastHash { .unwrap() } } + +#[cfg(test)] +mod test { + use strum::{EnumCount, FromRepr}; + + use super::HasDiscriminant; + + #[derive(Debug, Clone, FromRepr, EnumCount, PartialEq, Eq, PartialOrd, Ord)] + enum TestEnum { + A, + B, + C, + } + + impl HasDiscriminant for TestEnum { + fn discriminant(&self) -> usize { + self.clone() as usize + } + } + + #[test] + fn enum_variants_are_onto_discriminants() { + let mut variant_set = vec![]; + let mut uint_set = vec![]; + for u in 0..TestEnum::COUNT { + let variant = TestEnum::from_repr(u).unwrap(); + variant_set.push(variant.clone()); + uint_set.push(variant.discriminant()); + } + + variant_set.sort(); + variant_set.dedup(); + + uint_set.sort(); + uint_set.dedup(); + + assert_eq!(variant_set.len(), TestEnum::COUNT); + assert_eq!(uint_set.len(), TestEnum::COUNT); + } +}