From 25711315d46dc21654d4b0f5a0272ee720261d9a Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Tue, 29 Oct 2024 22:56:18 +0100 Subject: [PATCH] refactor: Drop block consensus program `TransactionIsValid` Use `transaction::SingleProof` instead. --- src/models/blockchain/block/block_appendix.rs | 10 + src/models/blockchain/block/block_kernel.rs | 11 +- src/models/blockchain/block/mod.rs | 10 + src/models/blockchain/block/validity.rs | 1 - .../block/validity/appendix_witness.rs | 27 ++- .../block/validity/block_program.rs | 8 +- .../block/validity/transaction_is_valid.rs | 223 ------------------ 7 files changed, 47 insertions(+), 243 deletions(-) delete mode 100644 src/models/blockchain/block/validity/transaction_is_valid.rs diff --git a/src/models/blockchain/block/block_appendix.rs b/src/models/blockchain/block/block_appendix.rs index 9822ad87..1cb2896a 100644 --- a/src/models/blockchain/block/block_appendix.rs +++ b/src/models/blockchain/block/block_appendix.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use arbitrary::Arbitrary; use get_size::GetSize; use serde::Deserialize; @@ -36,3 +38,11 @@ impl BlockAppendix { .collect() } } + +impl Deref for BlockAppendix { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.claims + } +} diff --git a/src/models/blockchain/block/block_kernel.rs b/src/models/blockchain/block/block_kernel.rs index 11f1ffdd..94631ce2 100644 --- a/src/models/blockchain/block/block_kernel.rs +++ b/src/models/blockchain/block/block_kernel.rs @@ -9,7 +9,7 @@ use tasm_lib::twenty_first::math::bfield_codec::BFieldCodec; use super::block_appendix::BlockAppendix; use super::block_body::BlockBody; use super::block_header::BlockHeader; -use super::validity::transaction_is_valid::TransactionIsValid; +use crate::models::blockchain::transaction::validity::single_proof::SingleProof; use crate::models::proof_abstractions::mast_hash::HasDiscriminant; use crate::models::proof_abstractions::mast_hash::MastHash; use crate::models::proof_abstractions::tasm::program::ConsensusProgram; @@ -26,8 +26,13 @@ pub struct BlockKernel { impl BlockKernel { pub(crate) fn new(header: BlockHeader, body: BlockBody) -> Self { // todo: populate appendix properly - let transaction_is_valid_claim = Claim::new(TransactionIsValid.hash()) - .with_input(body.mast_hash().reversed().values().to_vec()); + let transaction_is_valid_claim = Claim::new(SingleProof.hash()).with_input( + body.transaction_kernel + .mast_hash() + .reversed() + .values() + .to_vec(), + ); let appendix = BlockAppendix::new(vec![transaction_is_valid_claim]); Self { header, diff --git a/src/models/blockchain/block/mod.rs b/src/models/blockchain/block/mod.rs index 88956fa0..c8a5ab4f 100644 --- a/src/models/blockchain/block/mod.rs +++ b/src/models/blockchain/block/mod.rs @@ -50,6 +50,7 @@ use super::type_scripts::time_lock::TimeLock; use crate::config_models::network::Network; use crate::models::blockchain::block::difficulty_control::difficulty_control; use crate::models::blockchain::shared::Hash; +use crate::models::blockchain::transaction::validity::single_proof::SingleProof; use crate::models::proof_abstractions::mast_hash::MastHash; use crate::models::proof_abstractions::timestamp::Timestamp; use crate::models::state::wallet::address::ReceivingAddress; @@ -630,6 +631,15 @@ impl Block { return false; } + // 1.a)i: Verify that transaction validity is an appendix claim proven + // by the block proof. + let transaction_is_valid = + SingleProof::claim(block_copy.body().transaction_kernel.mast_hash()); + if !block_copy.appendix().contains(&transaction_is_valid) { + warn!("Block proof does not establish transaction validity."); + return false; + } + // 1.b) Check block size if self.size() > MAX_BLOCK_SIZE { warn!( diff --git a/src/models/blockchain/block/validity.rs b/src/models/blockchain/block/validity.rs index bc749eb1..6e031969 100644 --- a/src/models/blockchain/block/validity.rs +++ b/src/models/blockchain/block/validity.rs @@ -24,7 +24,6 @@ pub mod correct_mmr_update; pub mod correct_mutator_set_update; pub mod mmr_membership; pub mod predecessor_is_valid; -pub mod transaction_is_valid; /// The validity of a block, in the principal case, decomposes into these subclaims. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)] diff --git a/src/models/blockchain/block/validity/appendix_witness.rs b/src/models/blockchain/block/validity/appendix_witness.rs index 0eec92f9..bb386504 100644 --- a/src/models/blockchain/block/validity/appendix_witness.rs +++ b/src/models/blockchain/block/validity/appendix_witness.rs @@ -20,9 +20,9 @@ use tokio::sync::TryLockError; use super::block_primitive_witness::BlockPrimitiveWitness; use super::block_program::BlockProgram; -use super::transaction_is_valid::TransactionIsValid; -use super::transaction_is_valid::TransactionIsValidWitness; use crate::models::blockchain::block::block_body::BlockBody; +use crate::models::blockchain::transaction::validity::single_proof::SingleProof; +use crate::models::blockchain::transaction::TransactionProof; use crate::models::proof_abstractions::mast_hash::MastHash; use crate::models::proof_abstractions::tasm::program::ConsensusProgram; use crate::models::proof_abstractions::tasm::program::TritonProverSync; @@ -64,16 +64,23 @@ impl AppendixWitness { // TODO: Rename when used pub(crate) async fn _produce( block_primitive_witness: BlockPrimitiveWitness, - sync_device: &TritonProverSync, + _sync_device: &TritonProverSync, ) -> Result { - let block_mast_hash = block_primitive_witness.body().mast_hash(); + let txk_mast_hash = block_primitive_witness + .body() + .transaction_kernel + .mast_hash(); - let tx_is_valid_claim = TransactionIsValid::_claim(block_mast_hash); - let tx_is_valid_witness = TransactionIsValidWitness::from(block_primitive_witness.clone()); - let tx_is_valid_nondeterminism = tx_is_valid_witness.nondeterminism(); - let tx_is_valid_proof = TransactionIsValid - .prove(&tx_is_valid_claim, tx_is_valid_nondeterminism, sync_device) - .await?; + let tx_is_valid_claim = SingleProof::claim(txk_mast_hash); + let tx_is_valid_proof = match &block_primitive_witness.transaction.proof { + TransactionProof::SingleProof(proof) => proof.clone(), + _ => { + panic!( + "can only produce appendix witness from single-proof transaction; got {:?}", + block_primitive_witness.transaction.proof + ); + } + }; // todo: add other claims and proofs diff --git a/src/models/blockchain/block/validity/block_program.rs b/src/models/blockchain/block/validity/block_program.rs index 770185fe..69fed085 100644 --- a/src/models/blockchain/block/validity/block_program.rs +++ b/src/models/blockchain/block/validity/block_program.rs @@ -43,7 +43,7 @@ impl BlockProgram { impl ConsensusProgram for BlockProgram { fn source(&self) { - let block_body_digest: Digest = tasmlib::tasmlib_io_read_stdin___digest(); + let _block_body_digest: Digest = tasmlib::tasmlib_io_read_stdin___digest(); let start_address: BFieldElement = FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS; let block_witness: AppendixWitness = tasmlib::decode_from_memory(start_address); let claims: Vec = block_witness.claims; @@ -51,10 +51,6 @@ impl ConsensusProgram for BlockProgram { let mut i = 0; while i < claims.len() { - assert_eq!( - claims[i].input, - block_body_digest.reversed().values().to_vec() - ); tasmlib::tasmlib_io_write_to_stdout___digest(Tip5::hash(&claims[i])); verify_stark(Stark::default(), &claims[i], &proofs[i]); @@ -207,7 +203,7 @@ pub(crate) mod test { assert_eq!(rust_output, tasm_output); let expected_output = appendix_witness - .claims + .claims() .iter() .flat_map(|appendix_claim| Tip5::hash(appendix_claim).values().to_vec()) .collect_vec(); diff --git a/src/models/blockchain/block/validity/transaction_is_valid.rs b/src/models/blockchain/block/validity/transaction_is_valid.rs deleted file mode 100644 index 6aa69791..00000000 --- a/src/models/blockchain/block/validity/transaction_is_valid.rs +++ /dev/null @@ -1,223 +0,0 @@ -use strum::EnumCount; -use tasm_lib::field; -use tasm_lib::hashing::algebraic_hasher::hash_varlen::HashVarlen; -use tasm_lib::hashing::merkle_verify::MerkleVerify; -use tasm_lib::memory::encode_to_memory; -use tasm_lib::memory::FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS; -use tasm_lib::prelude::Library; -use tasm_lib::prelude::TasmObject; -use tasm_lib::triton_vm::isa::triton_asm; -use tasm_lib::triton_vm::prelude::BFieldCodec; -use tasm_lib::triton_vm::prelude::BFieldElement; -use tasm_lib::triton_vm::prelude::LabelledInstruction; -use tasm_lib::triton_vm::prelude::Program; -use tasm_lib::triton_vm::prelude::Tip5; -use tasm_lib::triton_vm::proof::Claim; -use tasm_lib::triton_vm::proof::Proof; -use tasm_lib::triton_vm::stark::Stark; -use tasm_lib::triton_vm::vm::NonDeterminism; -use tasm_lib::triton_vm::vm::PublicInput; -use tasm_lib::twenty_first::prelude::AlgebraicHasher; -use tasm_lib::verifier::stark_verify::StarkVerify; -use tasm_lib::Digest; - -use super::block_primitive_witness::BlockPrimitiveWitness; -use crate::models::blockchain::block::block_body::BlockBodyField; -use crate::models::blockchain::transaction::validity::single_proof::SingleProof; -use crate::models::blockchain::transaction::validity::tasm::claims::generate_single_proof_claim::GenerateSingleProofClaim; -use crate::models::blockchain::transaction::TransactionProof; -use crate::models::proof_abstractions::mast_hash::MastHash; -use crate::models::proof_abstractions::tasm::builtins::{self as tasmlib}; -use crate::models::proof_abstractions::tasm::program::ConsensusProgram; -use crate::models::proof_abstractions::SecretWitness; - -#[derive(Debug, Clone, BFieldCodec, TasmObject)] -pub(crate) struct TransactionIsValidWitness { - single_proof: Proof, - mast_path_txk: Vec, - txk_mast_hash: Digest, -} - -impl From for TransactionIsValidWitness { - fn from(block_primitive_witness: BlockPrimitiveWitness) -> Self { - let block_body = block_primitive_witness.body(); - let mast_path_txk = block_body.mast_path(BlockBodyField::TransactionKernel); - let TransactionProof::SingleProof(single_proof) = - &block_primitive_witness.transaction.proof - else { - panic!("cannot make a block whose transaction is not supported by a single proof"); - }; - let txk_mast_hash = block_body.transaction_kernel.mast_hash(); - Self { - single_proof: single_proof.to_owned(), - mast_path_txk, - txk_mast_hash, - } - } -} - -impl SecretWitness for TransactionIsValidWitness { - fn standard_input(&self) -> PublicInput { - self.txk_mast_hash.reversed().values().to_vec().into() - } - - fn program(&self) -> Program { - Program::new(&TransactionIsValid.code()) - } - - fn nondeterminism(&self) -> NonDeterminism { - let mut nondeterminism = NonDeterminism::new([]); - - encode_to_memory( - &mut nondeterminism.ram, - FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS, - self, - ); - nondeterminism - .digests - .extend_from_slice(&self.mast_path_txk); - - let claim = SingleProof::claim(self.txk_mast_hash); - StarkVerify::new_with_dynamic_layout(Stark::default()).update_nondeterminism( - &mut nondeterminism, - &self.single_proof, - &claim, - ); - - nondeterminism - } -} - -#[derive(Debug, Clone)] -pub(crate) struct TransactionIsValid; - -impl TransactionIsValid { - // TODO: Rename when used - pub(crate) fn _claim(block_mast_hash: Digest) -> Claim { - let input = block_mast_hash.reversed().values().to_vec(); - - Claim::new(Self.hash()).with_input(input) - } -} - -impl ConsensusProgram for TransactionIsValid { - fn source(&self) { - let block_body_mast_hash: Digest = tasmlib::tasmlib_io_read_stdin___digest(); - let start_address: BFieldElement = FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS; - let witness: TransactionIsValidWitness = tasmlib::decode_from_memory(start_address); - - tasmlib::tasmlib_hashing_merkle_verify( - block_body_mast_hash, - BlockBodyField::TransactionKernel as u32, - Tip5::hash_varlen(&witness.txk_mast_hash.encode()), - BlockBodyField::COUNT.next_power_of_two().ilog2(), - ); - - let claim = SingleProof::claim(witness.txk_mast_hash); - tasmlib::verify_stark(Stark::default(), &claim, &witness.single_proof); - } - - fn code(&self) -> Vec { - let mut library = Library::new(); - - let merkle_verify = library.import(Box::new(MerkleVerify)); - let hash_varlen = library.import(Box::new(HashVarlen)); - let witness_field_txkmh = field!(TransactionIsValidWitness::txk_mast_hash); - let witness_field_single_proof = field!(TransactionIsValidWitness::single_proof); - let authenticate_txkmh = triton_asm! { - // _ [bbmh] *witness - - push {BlockBodyField::COUNT.next_power_of_two().ilog2()} - push {BlockBodyField::TransactionKernel as u32} - pick 2 - - {&witness_field_txkmh} - // _ [bbmh] height index *txkmh - - push {Digest::LEN} - call {hash_varlen} - // _ [bbmh] height index [txkmh_as_leaf] - - call {merkle_verify} - // _ - - push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS} - {&witness_field_txkmh} - addi {Digest::LEN - 1} read_mem {Digest::LEN} pop 1 - // _ [txkmh] - }; - - let push_single_proof_program_digest = { - let [d0, d1, d2, d3, d4] = SingleProof.hash().values(); - triton_asm! { - push {d4} - push {d3} - push {d2} - push {d1} - push {d0} - } - }; - - let generate_single_proof_claim = library.import(Box::new(GenerateSingleProofClaim)); - let stark_verify = library.import(Box::new(StarkVerify::new_with_dynamic_layout( - Stark::default(), - ))); - - let main = triton_asm! { - // _ - - read_io 5 - // _ [block_body_mast_hash] - - push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS} - hint tx_is_valid_witness = stack[0] - // _ [bbmh] *witness - - {&authenticate_txkmh} - // _ [txkmh] - - {&push_single_proof_program_digest} - // _ [txkmh] [single_proof_program_digest] - - call {generate_single_proof_claim} - // _ *claim - - push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS} - {&witness_field_single_proof} - // _ *claim *proof - - call {stark_verify} - // _ - - halt - }; - - let imports = library.all_imports(); - triton_asm! { - {&main} - {&imports} - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::models::blockchain::block::validity::block_primitive_witness::test::deterministic_block_primitive_witness; - - #[test] - fn transaction_is_valid_halts_gracefully() { - let block_primitive_witness = deterministic_block_primitive_witness(); - let block_body_mast_hash = block_primitive_witness.body().mast_hash(); - let transaction_is_valid_witness = TransactionIsValidWitness::from(block_primitive_witness); - - let input = block_body_mast_hash.reversed().values().to_vec().into(); - let nondeterminism = transaction_is_valid_witness.nondeterminism(); - let rust_result = TransactionIsValid - .run_rust(&input, nondeterminism.clone()) - .unwrap(); - let tasm_result = TransactionIsValid.run_tasm(&input, nondeterminism).unwrap(); - - assert_eq!(rust_result, tasm_result); - } -}