Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce TransactionVerifier #180

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions miden-tx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ miden-core = { package = "miden-core", git = "https://github.com/0xPolygonMiden/
miden-objects = { package = "miden-objects", path = "../objects", default-features = false }
miden-lib = { package = "miden-lib", path = "../miden-lib" }
miden-stdlib = { package = "miden-stdlib", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false }
miden-verifier = { package = "miden-verifier", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false }

[dev-dependencies]
miden-objects = { package = "miden-objects", path = "../objects", default-features = false }
Expand Down
149 changes: 94 additions & 55 deletions miden-tx/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::{
Digest, MidenLib, ModuleAst, Note, NoteScript, Operation, Program, ProgramAst, SatKernel,
StdLibrary, TransactionCompilerError,
};
use miden_core::ProgramInfo;

#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -169,24 +170,22 @@ impl TransactionComplier {
let mut assembly_context = AssemblyContext::new(AssemblyContextType::Program);

// Create note tree and note [CodeBlock]s
let (note_tree_root, note_roots) =
self.create_note_program_tree(&target_account_interface, notes, &mut assembly_context)?;
let (note_tree_root, note_roots) = self.compile_and_build_note_program_tree(
&target_account_interface,
notes,
&mut assembly_context,
)?;

// Create the transaction program
let (tx_script_code_block, tx_script_hash) =
self.create_tx_program(tx_script, &mut assembly_context, target_account_interface)?;
let (tx_script_code_block, tx_script_hash) = self.create_tx_script_program(
tx_script,
&mut assembly_context,
target_account_interface,
)?;

// Merge transaction script code block and epilogue code block
let tx_script_and_epilogue = CodeBlock::new_join([
CodeBlock::new_call(tx_script_code_block.hash()),
self.epilogue.clone(),
]);

// Merge prologue and note script tree
let prologue_and_notes = CodeBlock::new_join([self.prologue.clone(), note_tree_root]);

// Merge prologue, note tree, tx script and epilogue
let program_root = CodeBlock::new_join([prologue_and_notes, tx_script_and_epilogue]);
// build transaction program
let program_root =
self.build_transaction_program(note_tree_root, tx_script_code_block.hash());

// Create [CodeBlockTable] from [AssemblyContext]
let mut cb_table = self
Expand All @@ -202,27 +201,41 @@ impl TransactionComplier {
// insert transaction script into [CodeBlockTable]
cb_table.insert(tx_script_code_block);

// Create transaction program
// Create transaction program with kernel
let program = Program::with_kernel(program_root, self.assembler.kernel().clone(), cb_table);

// Create compiled transaction
Ok((program, tx_script_hash))
}

/// Returns a [ProgramInfo] which contains the hash of the transaction program associated with
/// the provided consumed note script hashes and transaction script hash.
pub fn build_program_info(
&self,
note_script_hashes: Vec<Digest>,
tx_script_hash: Option<Digest>,
) -> ProgramInfo {
let tx_script_hash =
tx_script_hash.unwrap_or(CodeBlock::new_span(vec![Operation::Noop]).hash());
let note_tree_root = self.build_note_program_tree(note_script_hashes);
let transaction_program = self.build_transaction_program(note_tree_root, tx_script_hash);
ProgramInfo::new(transaction_program.hash(), self.assembler.kernel().clone())
}

// HELPER METHODS
// --------------------------------------------------------------------------------------------

/// Returns a [CodeBlock] which contains the note program tree root and a [Vec<CodeBlock>] which
/// contains the [CodeBlock]s associated with the notes.
fn create_note_program_tree(
fn compile_and_build_note_program_tree(
&mut self,
target_account_interface: &[Digest],
notes: &[Note],
assembly_context: &mut AssemblyContext,
) -> Result<(CodeBlock, Vec<CodeBlock>), TransactionCompilerError> {
// Create vectors to store note programs and note roots
let mut note_script_hashes = Vec::new();
let mut note_programs = Vec::new();
let mut note_roots = Vec::new();

// Create and verify note programs. Note programs are verified against the target account.
for note in notes.iter() {
Expand All @@ -235,52 +248,21 @@ impl TransactionComplier {
TransactionCompilerError::NoteIncompatibleWithAccountInterface(note_root.hash())
},
)?;
note_programs.push(CodeBlock::new_join([
self.note_setup.clone(),
CodeBlock::new_call(note_root.hash()),
]));
note_roots.push(note_root);
note_script_hashes.push(note_root.hash());
note_programs.push(note_root);
}

// Push note processing teardown onto the note programs vector
note_programs.push(self.note_processing_teardown.clone());

// Merge the note programs into a tree using join blocks
while note_programs.len() != 1 {
// TODO: We should optimize this in the future - however maybe not required as this
// part will be handled by a pcall-like operation in the future.
// Pad note programs to an even number using a [Operation::Noop] span block
if note_programs.len() % 2 != 0 {
note_programs.push(CodeBlock::new_span(vec![Operation::Noop]));
}
// build the note program tree
let note_tree_root = self.build_note_program_tree(note_script_hashes);

// convert vector into an iterator
let mut note_programs_iter = note_programs.into_iter();

// create a temporary vector to hold the merged CodeBlocks
let mut note_programs_temp = Vec::new();

// Consume two code blocks at a time and merge them into a single code block
while let (Some(left_code_block), Some(right_code_block)) =
(note_programs_iter.next(), note_programs_iter.next())
{
note_programs_temp.push(CodeBlock::new_join([left_code_block, right_code_block]));
}

note_programs = note_programs_temp;
}

Ok((
note_programs.into_iter().next().expect("a single root code block exists"),
note_roots,
))
Ok((note_tree_root, note_programs))
}

/// Returns a ([CodeBlock], Option<Digest>) tuple where the first element is the compiled
/// transaction script program and the second element is the hash of the transaction script
/// program. If no transaction script is provided, the first element is a [CodeBlock] containing
/// a single [Operation::Noop] and the second element is `None`.
fn create_tx_program(
fn create_tx_script_program(
&mut self,
tx_script: Option<ProgramAst>,
assembly_context: &mut AssemblyContext,
Expand Down Expand Up @@ -322,6 +304,63 @@ impl TransactionComplier {
NoteTarget::Procedures(procs) => Ok(procs),
}
}

/// Returns a [CodeBlock] which represents the transaction program.
fn build_transaction_program(
&self,
note_program_tree: CodeBlock,
tx_script_hash: Digest,
) -> CodeBlock {
// Merge transaction script code block and epilogue code block
let tx_script_and_epilogue =
CodeBlock::new_join([CodeBlock::new_call(tx_script_hash), self.epilogue.clone()]);

// Merge prologue and note script tree
let prologue_and_notes = CodeBlock::new_join([self.prologue.clone(), note_program_tree]);

// Merge prologue, note tree, tx script and epilogue
CodeBlock::new_join([prologue_and_notes, tx_script_and_epilogue])
}

/// Returns a [CodeBlock] which represents the note program tree.
fn build_note_program_tree(&self, note_script_hashes: Vec<Digest>) -> CodeBlock {
let mut note_programs = note_script_hashes
.into_iter()
.map(|note_hash| {
CodeBlock::new_join([self.note_setup.clone(), CodeBlock::new_call(note_hash)])
})
.collect::<Vec<_>>();

// Push note processing teardown onto the note programs vector
note_programs.push(self.note_processing_teardown.clone());

// Merge the note programs into a tree using join blocks
while note_programs.len() != 1 {
// TODO: We should optimize this in the future - however maybe not required as this
// part will be handled by a pcall-like operation in the future.
// Pad note programs to an even number using a [Operation::Noop] span block
if note_programs.len() % 2 != 0 {
note_programs.push(CodeBlock::new_span(vec![Operation::Noop]));
}

// convert vector into an iterator
let mut note_programs_iter = note_programs.into_iter();

// create a temporary vector to hold the merged CodeBlocks
let mut note_programs_temp = Vec::new();

// Consume two code blocks at a time and merge them into a single code block
while let (Some(left_code_block), Some(right_code_block)) =
(note_programs_iter.next(), note_programs_iter.next())
{
note_programs_temp.push(CodeBlock::new_join([left_code_block, right_code_block]));
}

note_programs = note_programs_temp;
}

note_programs.into_iter().next().expect("a single root code block exists")
}
}

impl Default for TransactionComplier {
Expand Down
7 changes: 7 additions & 0 deletions miden-tx/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::{
TransactionResultError,
};
use miden_objects::TransactionWitnessError;
use miden_verifier::VerificationError;

#[derive(Debug)]
pub enum TransactionError {
Expand Down Expand Up @@ -42,6 +43,12 @@ pub enum TransactionProverError {
CorruptTransactionWitnessConsumedNoteData(TransactionWitnessError),
}

#[derive(Debug)]
pub enum TransactionVerifierError {
TransactionVerificationFailed(VerificationError),
InsufficientProofSecurityLevel(u32, u32),
}

#[derive(Debug)]
pub enum DataStoreError {
AccountNotFound(AccountId),
Expand Down
5 changes: 4 additions & 1 deletion miden-tx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use assembly::{
ast::{ModuleAst, ProgramAst},
Assembler, AssemblyContext, AssemblyContextType, AssemblyError,
};
use crypto::{hash::rpo::RpoDigest as Digest, merkle::NodeIndex};
use crypto::{hash::rpo::Rpo256 as Hasher, hash::rpo::RpoDigest as Digest, merkle::NodeIndex};
use miden_core::{code_blocks::CodeBlock, utils::collections::BTreeMap, Operation, Program};
use miden_lib::{MidenLib, SatKernel};
use miden_objects::{
Expand All @@ -22,10 +22,13 @@ mod executor;
pub use error::TransactionError;
use error::{
DataStoreError, TransactionCompilerError, TransactionExecutorError, TransactionProverError,
TransactionVerifierError,
};
pub use executor::TransactionExecutor;
mod prover;
pub use prover::TransactionProver;
mod verifier;
pub use verifier::TransactionVerifier;

#[cfg(test)]
mod tests;
4 changes: 1 addition & 3 deletions miden-tx/src/prover/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl TransactionProver {
let created_notes = CreatedNotes::try_from_vm_result(&outputs, &stack, &map, &store)
.map_err(TransactionProverError::TransactionResultError)?;

let (account, block_header, _chain, consumed_notes, tx_program, tx_script_root) =
let (account, block_header, _chain, consumed_notes, _tx_program, tx_script_root) =
transaction.into_parts();

Ok(ProvenTransaction::new(
Expand All @@ -61,7 +61,6 @@ impl TransactionProver {
consumed_notes.into(),
created_notes.into(),
tx_script_root,
tx_program.hash(),
block_header.hash(),
proof,
))
Expand Down Expand Up @@ -112,7 +111,6 @@ impl TransactionProver {
consumed_notes_info,
created_notes.into(),
tx_script_root,
tx_program.hash(),
block_hash,
proof,
))
Expand Down
26 changes: 7 additions & 19 deletions miden-tx/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use super::{
Account, AccountId, BlockHeader, ChainMmr, DataStore, DataStoreError, Note, NoteOrigin,
TransactionExecutor, TransactionProver,
TransactionExecutor, TransactionProver, TransactionVerifier,
};
use assembly::{
ast::{ModuleAst, ProgramAst},
Assembler,
};
use crypto::{StarkField, ONE};
use miden_core::ProgramInfo;
use miden_objects::{
mock::{
mock_inputs, prepare_word, CHILD_ROOT_PARENT_LEAF_INDEX, CHILD_SMT_DEPTH,
Expand Down Expand Up @@ -249,7 +248,7 @@ fn test_transaction_result_account_delta() {
}

#[test]
fn test_prove_witness() {
fn test_prove_witness_and_verify() {
let data_store = MockDataStore::new();
let mut executor = TransactionExecutor::new(data_store.clone());

Expand All @@ -272,9 +271,10 @@ fn test_prove_witness() {
// prove the transaction with the witness
let proof_options = ProvingOptions::default();
let prover = TransactionProver::new(proof_options);
let proven_transaction = prover.prove_transaction_witness(witness);
let proven_transaction = prover.prove_transaction_witness(witness).unwrap();

assert!(proven_transaction.is_ok());
let verifier = TransactionVerifier::new(96);
assert!(verifier.verify(proven_transaction).is_ok());
}

#[test]
Expand All @@ -297,23 +297,11 @@ fn test_prove_and_verify_with_tx_executor() {
.prepare_transaction(account_id, block_ref, &note_origins, None)
.unwrap();

// extract transaction data for later consumption
let program_hash = prepared_transaction.tx_program().hash();
let kernel = prepared_transaction.tx_program().kernel().clone();

// prove transaction
let proof_options = ProvingOptions::default();
let prover = TransactionProver::new(proof_options);
let proven_transaction = prover.prove_prepared_transaction(prepared_transaction).unwrap();

let stack_inputs = proven_transaction.build_stack_inputs();
let stack_outputs = proven_transaction.build_stack_outputs();
let program_info = ProgramInfo::new(program_hash, kernel);
let result = miden_verifier::verify(
program_info,
stack_inputs,
stack_outputs,
proven_transaction.proof().clone(),
);
assert!(result.is_ok());
let verifier = TransactionVerifier::new(96);
assert!(verifier.verify(proven_transaction).is_ok());
}
Loading
Loading