From 20446b715ebc530f3223c69e9c1cfcb1d24352b0 Mon Sep 17 00:00:00 2001 From: Philippe Laferriere Date: Tue, 10 Sep 2024 14:12:45 -0400 Subject: [PATCH] Incorporate assert arguments into eq_hash --- assembly/src/assembler/mast_forest_builder.rs | 28 ++++- assembly/src/tests.rs | 112 ++++++++++++++++++ 2 files changed, 135 insertions(+), 5 deletions(-) diff --git a/assembly/src/assembler/mast_forest_builder.rs b/assembly/src/assembler/mast_forest_builder.rs index 82316e941..0aa8d1001 100644 --- a/assembly/src/assembler/mast_forest_builder.rs +++ b/assembly/src/assembler/mast_forest_builder.rs @@ -450,17 +450,35 @@ impl MastForestBuilder { fn eq_hash_for_node(&self, node: &MastNode) -> EqHash { match node { MastNode::Block(node) => { - let mut decorator_bytes_to_hash = Vec::new(); + let mut bytes_to_hash = Vec::new(); for &(idx, decorator_id) in node.decorators() { - decorator_bytes_to_hash.extend(idx.to_le_bytes()); - decorator_bytes_to_hash.extend(self[decorator_id].eq_hash().as_bytes()); + bytes_to_hash.extend(idx.to_le_bytes()); + bytes_to_hash.extend(self[decorator_id].eq_hash().as_bytes()); } - if decorator_bytes_to_hash.is_empty() { + // Add any `Assert` or `U32assert2` opcodes present, since these are not included in + // the MAST root. + for (op_idx, op) in node.operations().enumerate() { + if let Operation::U32assert2(inner_value) | Operation::Assert(inner_value) = op + { + let op_idx: u32 = op_idx + .try_into() + .expect("there are more than 2^{32}-1 operations in basic block"); + + // we include the opcode to differentiate between `Assert` and `U32assert2` + bytes_to_hash.push(op.op_code()); + // we include the operation index to distinguish between basic blocks that + // would have the same assert instructions, but in a different order + bytes_to_hash.extend(op_idx.to_le_bytes()); + bytes_to_hash.extend(inner_value.to_le_bytes()); + } + } + + if bytes_to_hash.is_empty() { EqHash::new(node.digest()) } else { - let decorator_root = Blake3_256::hash(&decorator_bytes_to_hash); + let decorator_root = Blake3_256::hash(&bytes_to_hash); EqHash::with_decorator_root(node.digest(), decorator_root) } }, diff --git a/assembly/src/tests.rs b/assembly/src/tests.rs index 37086ccae..5909fae25 100644 --- a/assembly/src/tests.rs +++ b/assembly/src/tests.rs @@ -1477,6 +1477,118 @@ end"; Ok(()) } +/// Ensure that there is no collision between `u32assert` instructions with different error codes. +#[test] +fn u32assert_with_code_in_duplicate_procedure() -> TestResult { + let context = TestContext::default(); + let source = source_file!( + &context, + "\ + proc.f1 + u32assert.err=1 + end + proc.f2 + u32assert.err=2 + end + proc.f12 + u32assert.err=1 + u32assert.err=2 + end + proc.f21 + u32assert.err=2 + u32assert.err=1 + end + proc.g1 + assert.err=1 + end + proc.g2 + assert.err=2 + end + proc.g12 + assert.err=1 + assert.err=2 + end + proc.g21 + assert.err=2 + assert.err=1 + end + proc.fg + assert.err=1 + u32assert.err=1 + assert.err=2 + u32assert.err=2 + + u32assert.err=1 + assert.err=1 + u32assert.err=2 + assert.err=2 + end + + begin + exec.f1 + exec.f2 + exec.f12 + exec.f21 + exec.g1 + exec.g2 + exec.g12 + exec.g21 + exec.fg + end + " + ); + let program = context.assemble(source)?; + + let expected = "\ +begin + basic_block + pad + u32assert2(1) + drop + pad + u32assert2(2) + drop + pad + u32assert2(1) + drop + pad + u32assert2(2) + drop + pad + u32assert2(2) + drop + pad + u32assert2(1) + drop + assert(1) + assert(2) + assert(1) + assert(2) + assert(2) + assert(1) + assert(1) + pad + u32assert2(1) + drop + assert(2) + pad + u32assert2(2) + drop + pad + u32assert2(1) + drop + assert(1) + pad + u32assert2(2) + drop + assert(2) + end +end"; + + assert_str_eq!(expected, format!("{program}")); + Ok(()) +} + #[test] fn mtree_verify_with_code() -> TestResult { let context = TestContext::default();