Skip to content

Commit

Permalink
feat(Tx): (Verify capability to) produce ProofCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
aszepieniec committed Jul 18, 2024
1 parent 935d7fd commit dac7d9c
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 64 deletions.
6 changes: 5 additions & 1 deletion benches/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ mod transaction {
.new_tree(&mut test_runner)
.unwrap()
.current();
let nc_witness = NativeCurrencyWitness::from(primitive_witness);
let nc_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
bench_and_profile_consensus_program(
NativeCurrency,
&nc_witness.standard_input(),
Expand Down
2 changes: 1 addition & 1 deletion src/models/blockchain/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use tracing::error;
use twenty_first::math::b_field_element::BFieldElement;
use twenty_first::math::bfield_codec::BFieldCodec;
use twenty_first::util_types::algebraic_hasher::AlgebraicHasher;
use validity::standard_decomposition::ProofCollection;
use validity::proof_collection::ProofCollection;

use self::primitive_witness::PrimitiveWitness;
use self::transaction_kernel::TransactionKernel;
Expand Down
44 changes: 23 additions & 21 deletions src/models/blockchain/transaction/primitive_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use tracing::{debug, warn};

use crate::{
models::{
blockchain::type_scripts::{native_currency::NativeCurrency, neptune_coins::NeptuneCoins},
blockchain::type_scripts::{
native_currency::NativeCurrencyWitness, neptune_coins::NeptuneCoins, TypeScriptWitness,
},
proof_abstractions::tasm::program::ConsensusProgram,
},
util_types::mutator_set::commit,
Expand Down Expand Up @@ -143,18 +145,10 @@ impl PrimitiveWitness {
.iter()
.map(|address_seed| generation_address::SpendingKey::derive_from_seed(*address_seed))
.collect_vec();
let input_lock_scripts = input_spending_keys
.iter()
.map(|spending_key| spending_key.to_address().lock_script())
.collect_vec();
let input_lock_script_witnesses = input_spending_keys
.iter()
.map(|spending_key| spending_key.unlock_key.values().to_vec())
.collect_vec();
let input_lock_scripts_and_witnesses = input_lock_scripts

let input_lock_scripts_and_witnesses = input_spending_keys
.into_iter()
.zip(input_lock_script_witnesses)
.map(|(ls, wt)| LockScriptAndWitness::new_with_tokens(ls.program, wt))
.map(|spending_key| spending_key.lock_script_and_witness())
.collect_vec();

let input_utxos = input_lock_scripts_and_witnesses
Expand Down Expand Up @@ -632,24 +626,32 @@ impl PrimitiveWitness {
mutator_set_hash: mutator_set_accumulator.hash(),
};

let salted_input_utxos = SaltedUtxos {
utxos: input_utxos.clone(),
salt: inputs_salt.clone().try_into().unwrap(),
};
let salted_output_utxos = SaltedUtxos {
utxos: output_utxos.clone(),
salt: outputs_salt.clone().try_into().unwrap(),
};

let type_scripts_and_witnesses = if num_inputs + num_outputs > 0 {
vec![TypeScriptAndWitness::new(NativeCurrency.program())]
let native_currency_type_script_witness = NativeCurrencyWitness {
salted_input_utxos: salted_input_utxos.clone(),
salted_output_utxos: salted_output_utxos.clone(),
kernel: kernel.clone(),
};
vec![native_currency_type_script_witness.type_script_and_witness()]
} else {
vec![]
};

PrimitiveWitness {
lock_scripts_and_witnesses: input_lock_scripts_and_witnesses,
input_utxos: SaltedUtxos {
utxos: input_utxos.clone(),
salt: inputs_salt.clone().try_into().unwrap(),
},
input_utxos: salted_input_utxos,
input_membership_proofs: input_membership_proofs.clone(),
type_scripts_and_witnesses,
output_utxos: SaltedUtxos {
utxos: output_utxos.clone(),
salt: outputs_salt.clone().try_into().unwrap(),
},
output_utxos: salted_output_utxos,
output_sender_randomnesses: output_sender_randomnesses.clone(),
output_receiver_digests: output_receiver_preimages
.iter()
Expand Down
2 changes: 1 addition & 1 deletion src/models/blockchain/transaction/validity/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub mod collect_lock_scripts;
pub mod collect_type_scripts;
pub mod kernel_to_outputs;
pub mod proof_collection;
pub mod removal_records_integrity;
pub mod standard_decomposition;
pub mod tasm;
pub mod transaction_validity;
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use crate::models::{
};

use super::{
kernel_to_outputs::KernelToOutputsWitness, removal_records_integrity::RemovalRecordsIntegrity,
collect_type_scripts::CollectTypeScriptsWitness, kernel_to_outputs::KernelToOutputsWitness,
removal_records_integrity::RemovalRecordsIntegrity,
};

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec, TasmObject)]
Expand All @@ -55,13 +56,35 @@ pub struct ProofCollection {
}

impl ProofCollection {
pub fn produce(primitive_witness: PrimitiveWitness) -> Self {
fn extract_specific_witnesses(
primitive_witness: &PrimitiveWitness,
) -> (
RemovalRecordsIntegrityWitness,
CollectLockScriptsWitness,
KernelToOutputsWitness,
CollectTypeScriptsWitness,
) {
// collect witnesses
let removal_records_integrity_witness =
RemovalRecordsIntegrityWitness::from(&primitive_witness);
let collect_lock_scripts_witness = CollectLockScriptsWitness::from(&primitive_witness);
let kernel_to_outputs_witness = KernelToOutputsWitness::from(&primitive_witness);
let collect_type_scripts_witness = KernelToOutputsWitness::from(&primitive_witness);
RemovalRecordsIntegrityWitness::from(primitive_witness);
let collect_lock_scripts_witness = CollectLockScriptsWitness::from(primitive_witness);
let kernel_to_outputs_witness = KernelToOutputsWitness::from(primitive_witness);
let collect_type_scripts_witness = CollectTypeScriptsWitness::from(primitive_witness);

(
removal_records_integrity_witness,
collect_lock_scripts_witness,
kernel_to_outputs_witness,
collect_type_scripts_witness,
)
}
pub fn can_produce(primitive_witness: &PrimitiveWitness) -> bool {
let (
removal_records_integrity_witness,
collect_lock_scripts_witness,
kernel_to_outputs_witness,
collect_type_scripts_witness,
) = Self::extract_specific_witnesses(primitive_witness);

// verify graceful halts
let removal_records_integrity_halts = RemovalRecordsIntegrity
Expand Down Expand Up @@ -106,15 +129,26 @@ impl ProofCollection {
.iter()
.all(|ts| ts.halts_gracefully(txk_mast_hash, salted_inputs_hash, salted_outputs_hash));

if !removal_records_integrity_halts
|| !collect_lock_scripts_halts
|| !kernel_to_outputs_halts
|| !collect_type_scripts_halts
|| !all_lock_scripts_halt
|| !all_type_scripts_halt
{
panic!("cannot produce proof collection for transaction because one or more consensus programs fails to halt gracefully")
}
removal_records_integrity_halts
|| collect_lock_scripts_halts
|| kernel_to_outputs_halts
|| collect_type_scripts_halts
|| all_lock_scripts_halt
|| all_type_scripts_halt
}

pub fn produce(primitive_witness: &PrimitiveWitness) -> Self {
let (
removal_records_integrity_witness,
collect_lock_scripts_witness,
kernel_to_outputs_witness,
collect_type_scripts_witness,
) = Self::extract_specific_witnesses(primitive_witness);

let txk_mast_hash = primitive_witness.kernel.mast_hash();
let txk_mast_hash_as_input = PublicInput::new(txk_mast_hash.reversed().values().to_vec());
let salted_inputs_hash = Hash::hash(&primitive_witness.input_utxos);
let salted_outputs_hash = Hash::hash(&primitive_witness.output_utxos);

// prove
let removal_records_integrity = RemovalRecordsIntegrity.prove(
Expand Down Expand Up @@ -307,3 +341,31 @@ impl ConsensusProgram for StandardDecomposition {
todo!()
}
}

#[cfg(test)]
pub mod test {
use super::*;
use proptest::prop_assert;
use proptest::{arbitrary::Arbitrary, strategy::Strategy, test_runner::TestRunner};
use test_strategy::proptest;

#[proptest(cases = 5)]
fn can_produce_valid_collection(
#[strategy(PrimitiveWitness::arbitrary_with((2, 2, 2)))]
primitive_witness: PrimitiveWitness,
) {
prop_assert!(ProofCollection::can_produce(&primitive_witness));
}

#[test]
fn can_verify_valid_collection() {
let mut test_runner = TestRunner::deterministic();
let primitive_witness = PrimitiveWitness::arbitrary_with((2, 2, 2))
.new_tree(&mut test_runner)
.unwrap()
.current();
let proof_collection = ProofCollection::produce(&primitive_witness);
let txk_mast_hash = primitive_witness.kernel.mast_hash();
assert!(proof_collection.verify(txk_mast_hash));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ impl SecretWitness for RemovalRecordsIntegrityWitness {
PublicInput::new(self.mast_root.reversed().values().to_vec())
}

fn output(&self) -> Vec<BFieldElement> {
Hash::hash(&self.input_utxos).values().to_vec()
}

fn program(&self) -> triton_vm::prelude::Program {
RemovalRecordsIntegrity.program()
}
Expand Down Expand Up @@ -385,7 +389,6 @@ impl ConsensusProgram for RemovalRecordsIntegrity {
msmp.sender_randomness,
msmp.receiver_preimage.hash::<Hash>(),
);
println!("addition record: {}", addition_record.canonical_commitment);
tasmlib::mmr_verify_from_secret_in_leaf_index_on_stack(
&aocl.get_peaks(),
aocl.count_leaves(),
Expand Down Expand Up @@ -1252,9 +1255,7 @@ mod tests {
fn prop_positive(
removal_records_integrity_witness: RemovalRecordsIntegrityWitness,
) -> Result<(), TestCaseError> {
let salted_inputs_utxos_hash = Hash::hash(&removal_records_integrity_witness.input_utxos)
.values()
.to_vec();
let salted_inputs_utxos_hash = removal_records_integrity_witness.output();
let rust_result = RemovalRecordsIntegrity
.run_rust(
&removal_records_integrity_witness.standard_input(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tasm_lib::triton_vm::{instruction::LabelledInstruction, prelude::BFieldCodec

use crate::models::proof_abstractions::tasm::program::ConsensusProgram;

use super::standard_decomposition::ProofCollection;
use super::proof_collection::ProofCollection;

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, GetSize, BFieldCodec)]
pub struct TransactionValidity;
Expand Down
2 changes: 1 addition & 1 deletion src/models/blockchain/type_scripts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub trait TypeScriptWitness {
fn transaction_kernel(&self) -> TransactionKernel;
fn salted_input_utxos(&self) -> SaltedUtxos;
fn salted_output_utxos(&self) -> SaltedUtxos;

fn type_script_and_witness(&self) -> TypeScriptAndWitness;
fn type_script_standard_input(&self) -> PublicInput {
PublicInput::new(
[
Expand Down
51 changes: 36 additions & 15 deletions src/models/blockchain/type_scripts/native_currency.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::collections::HashMap;

use crate::models::blockchain::shared::Hash;
use crate::models::blockchain::transaction::primitive_witness::PrimitiveWitness;
use crate::models::blockchain::transaction::transaction_kernel::{
TransactionKernel, TransactionKernelField,
};
Expand All @@ -16,6 +15,7 @@ use crate::models::blockchain::transaction::utxo::Coin;
use crate::models::blockchain::transaction::utxo::Utxo;
use crate::models::blockchain::transaction::validity::tasm::coinbase_amount::CoinbaseAmount;
use crate::models::blockchain::type_scripts::BFieldCodec;
use crate::models::blockchain::type_scripts::TypeScriptAndWitness;
use crate::models::proof_abstractions::tasm::builtins as tasm;
use get_size::GetSize;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -584,15 +584,12 @@ impl TypeScriptWitness for NativeCurrencyWitness {
fn salted_output_utxos(&self) -> SaltedUtxos {
self.salted_output_utxos.clone()
}
}

impl From<PrimitiveWitness> for NativeCurrencyWitness {
fn from(primitive_witness: PrimitiveWitness) -> Self {
Self {
salted_input_utxos: primitive_witness.input_utxos.clone(),
salted_output_utxos: primitive_witness.output_utxos.clone(),
kernel: primitive_witness.kernel.clone(),
}
fn type_script_and_witness(&self) -> TypeScriptAndWitness {
TypeScriptAndWitness::new_with_nondeterminism(
NativeCurrency.program(),
self.nondeterminism(),
)
}
}

Expand Down Expand Up @@ -705,7 +702,11 @@ pub mod test {
.new_tree(&mut test_runner)
.unwrap()
.current();
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_positive(native_currency_witness).unwrap();
}

Expand All @@ -716,7 +717,11 @@ pub mod test {
.new_tree(&mut test_runner)
.unwrap()
.current();
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_positive(native_currency_witness).unwrap();
}

Expand All @@ -729,7 +734,11 @@ pub mod test {
primitive_witness: PrimitiveWitness,
) {
// PrimitiveWitness::arbitrary_with already ensures the transaction is balanced
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_positive(native_currency_witness)?;
}

Expand All @@ -741,7 +750,11 @@ pub mod test {
#[strategy(arbitrary_primitive_witness_with_timelocks(#_num_inputs, #_num_outputs, #_num_public_announcements))]
primitive_witness: PrimitiveWitness,
) {
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_positive(native_currency_witness)?;
}

Expand All @@ -762,7 +775,11 @@ pub mod test {
primitive_witness: PrimitiveWitness,
) {
// with high probability the amounts (which are random) do not add up
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_negative(
native_currency_witness,
&[InstructionError::AssertionFailed],
Expand All @@ -787,7 +804,11 @@ pub mod test {
primitive_witness: PrimitiveWitness,
) {
// with high probability the amounts (which are random) do not add up
let native_currency_witness = NativeCurrencyWitness::from(primitive_witness);
let native_currency_witness = NativeCurrencyWitness {
salted_input_utxos: primitive_witness.input_utxos,
salted_output_utxos: primitive_witness.output_utxos,
kernel: primitive_witness.kernel,
};
prop_negative(
native_currency_witness,
&[InstructionError::AssertionFailed],
Expand Down
3 changes: 3 additions & 0 deletions src/models/blockchain/type_scripts/time_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@ impl TypeScriptWitness for TimeLockWitness {
fn salted_output_utxos(&self) -> SaltedUtxos {
SaltedUtxos::empty()
}
fn type_script_and_witness(&self) -> TypeScriptAndWitness {
TypeScriptAndWitness::new_with_nondeterminism(TimeLock.program(), self.nondeterminism())
}
}

impl From<transaction::primitive_witness::PrimitiveWitness> for TimeLockWitness {
Expand Down
Loading

0 comments on commit dac7d9c

Please sign in to comment.