From 49761c13d61613ea88f9c8981e7ef6ff45c79ea0 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Mon, 10 Feb 2025 13:04:19 +0000 Subject: [PATCH 1/8] initial commit --- .../fuzz/fuzz_targets/parse-spends.rs | 11 ++++++++-- crates/chia-consensus/src/gen/conditions.rs | 22 +++++++++++++++++-- crates/chia-consensus/src/gen/flags.rs | 2 ++ crates/chia-consensus/src/gen/opcodes.rs | 2 ++ .../src/gen/validation_error.rs | 2 ++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs b/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs index 7e9dbdcf4..a0be1b18c 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs @@ -7,14 +7,21 @@ use chia_fuzz::{make_list, BitCursor}; use clvmr::{Allocator, NodePtr}; use chia_consensus::consensus_constants::TEST_CONSTANTS; -use chia_consensus::gen::flags::{NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT}; +use chia_consensus::gen::flags::{ + ENABLE_SHA256TREE_CONDITIONS, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT, +}; fuzz_target!(|data: &[u8]| { let mut a = Allocator::new(); let input = make_list(&mut a, &mut BitCursor::new(data)); // spends is a list of spends let input = a.new_pair(input, NodePtr::NIL).unwrap(); - for flags in &[0, STRICT_ARGS_COUNT, NO_UNKNOWN_CONDS] { + for flags in &[ + 0, + STRICT_ARGS_COUNT, + NO_UNKNOWN_CONDS, + ENABLE_SHA256TREE_CONDITIONS, + ] { let _ret = parse_spends::( &a, input, diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 39213398a..aa22ea80b 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -12,12 +12,12 @@ use super::opcodes::{ ASSERT_MY_BIRTH_SECONDS, ASSERT_MY_COIN_ID, ASSERT_MY_PARENT_ID, ASSERT_MY_PUZZLEHASH, ASSERT_PUZZLE_ANNOUNCEMENT, ASSERT_SECONDS_ABSOLUTE, ASSERT_SECONDS_RELATIVE, CREATE_COIN, CREATE_COIN_ANNOUNCEMENT, CREATE_COIN_COST, CREATE_PUZZLE_ANNOUNCEMENT, RECEIVE_MESSAGE, - REMARK, RESERVE_FEE, SEND_MESSAGE, SOFTFORK, + REMARK, RESERVE_FEE, SEND_MESSAGE, SOFTFORK, ASSERT_SHA256_TREE }; use super::sanitize_int::{sanitize_uint, SanitizedUint}; use super::validation_error::{first, next, rest, ErrorCode, ValidationErr}; use crate::consensus_constants::ConsensusConstants; -use crate::gen::flags::{DONT_VALIDATE_SIGNATURE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT}; +use crate::gen::flags::{DONT_VALIDATE_SIGNATURE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT, ENABLE_SHA256TREE_CONDITIONS}; use crate::gen::make_aggsig_final_message::u64_to_bytes; use crate::gen::messages::{Message, SpendId}; use crate::gen::spend_visitor::SpendVisitor; @@ -235,6 +235,9 @@ pub enum Condition { // this means the condition is unconditionally true and can be skipped Skip, SkipRelativeCondition, + + // assert sha256tree (SExp, 32 bytes) + AssertSha256Tree(NodePtr, NodePtr), } fn check_agg_sig_unsafe_message( @@ -581,6 +584,12 @@ pub fn parse_args( // this condition is always true, we always ignore arguments Ok(Condition::Skip) } + ASSERT_SHA256_TREE => { + maybe_check_args_terminator(a, c, flags)?; + let sexp = first(a, c)?; + let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertConcurrentPuzzleFailed)?; + Ok(Condition::AssertSha256Tree(sexp, id)) + } _ => Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)), } } @@ -1257,6 +1266,15 @@ pub fn parse_conditions( Condition::SkipRelativeCondition => { assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); } + Condition::AssertSha256Tree(sexp, hash) => { + if flags & ENABLE_SHA256TREE_CONDITIONS == 1{ + // for now we can validate this here + // in the future we might want to store the jobs and validate them more efficiently + if clvm_utils::tree_hash(&a, sexp).to_bytes() != a.atom(hash).as_ref() { + return Err(ValidationErr(c, ErrorCode::AssertSha256TreeFailed)); + } + } + } Condition::Skip => {} } } diff --git a/crates/chia-consensus/src/gen/flags.rs b/crates/chia-consensus/src/gen/flags.rs index b7c243298..1c9ca0902 100644 --- a/crates/chia-consensus/src/gen/flags.rs +++ b/crates/chia-consensus/src/gen/flags.rs @@ -19,3 +19,5 @@ pub const STRICT_ARGS_COUNT: u32 = 0x8_0000; pub const DONT_VALIDATE_SIGNATURE: u32 = 0x1_0000; pub const MEMPOOL_MODE: u32 = CLVM_MEMPOOL_MODE | NO_UNKNOWN_CONDS | STRICT_ARGS_COUNT; + +pub const ENABLE_SHA256TREE_CONDITIONS: u32 = 0x4_0000; diff --git a/crates/chia-consensus/src/gen/opcodes.rs b/crates/chia-consensus/src/gen/opcodes.rs index 30916086a..9748cead0 100644 --- a/crates/chia-consensus/src/gen/opcodes.rs +++ b/crates/chia-consensus/src/gen/opcodes.rs @@ -55,6 +55,8 @@ pub const ASSERT_BEFORE_SECONDS_ABSOLUTE: ConditionOpcode = 85; pub const ASSERT_BEFORE_HEIGHT_RELATIVE: ConditionOpcode = 86; pub const ASSERT_BEFORE_HEIGHT_ABSOLUTE: ConditionOpcode = 87; +pub const ASSERT_SHA256_TREE: ConditionOpcode = 91; + // no-op condition pub const REMARK: ConditionOpcode = 1; diff --git a/crates/chia-consensus/src/gen/validation_error.rs b/crates/chia-consensus/src/gen/validation_error.rs index a7cc9dfa2..14a520933 100644 --- a/crates/chia-consensus/src/gen/validation_error.rs +++ b/crates/chia-consensus/src/gen/validation_error.rs @@ -163,6 +163,7 @@ pub enum ErrorCode { InvalidMessageMode, InvalidCoinId, MessageNotSentOrReceived, + AssertSha256TreeFailed, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] @@ -359,6 +360,7 @@ impl From for u32 { ErrorCode::InvalidMessageMode => 145, ErrorCode::InvalidCoinId => 146, ErrorCode::MessageNotSentOrReceived => 147, + ErrorCode::AssertSha256TreeFailed => 148, } } } From f6fd9ceda16385e1d26b4a4b325ecd7ade392d31 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Mon, 10 Feb 2025 13:28:16 +0000 Subject: [PATCH 2/8] start adding tests --- crates/chia-consensus/src/gen/conditions.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index aa22ea80b..514dfe2e4 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -10,14 +10,16 @@ use super::opcodes::{ ASSERT_COIN_ANNOUNCEMENT, ASSERT_CONCURRENT_PUZZLE, ASSERT_CONCURRENT_SPEND, ASSERT_EPHEMERAL, ASSERT_HEIGHT_ABSOLUTE, ASSERT_HEIGHT_RELATIVE, ASSERT_MY_AMOUNT, ASSERT_MY_BIRTH_HEIGHT, ASSERT_MY_BIRTH_SECONDS, ASSERT_MY_COIN_ID, ASSERT_MY_PARENT_ID, ASSERT_MY_PUZZLEHASH, - ASSERT_PUZZLE_ANNOUNCEMENT, ASSERT_SECONDS_ABSOLUTE, ASSERT_SECONDS_RELATIVE, CREATE_COIN, - CREATE_COIN_ANNOUNCEMENT, CREATE_COIN_COST, CREATE_PUZZLE_ANNOUNCEMENT, RECEIVE_MESSAGE, - REMARK, RESERVE_FEE, SEND_MESSAGE, SOFTFORK, ASSERT_SHA256_TREE + ASSERT_PUZZLE_ANNOUNCEMENT, ASSERT_SECONDS_ABSOLUTE, ASSERT_SECONDS_RELATIVE, + ASSERT_SHA256_TREE, CREATE_COIN, CREATE_COIN_ANNOUNCEMENT, CREATE_COIN_COST, + CREATE_PUZZLE_ANNOUNCEMENT, RECEIVE_MESSAGE, REMARK, RESERVE_FEE, SEND_MESSAGE, SOFTFORK, }; use super::sanitize_int::{sanitize_uint, SanitizedUint}; use super::validation_error::{first, next, rest, ErrorCode, ValidationErr}; use crate::consensus_constants::ConsensusConstants; -use crate::gen::flags::{DONT_VALIDATE_SIGNATURE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT, ENABLE_SHA256TREE_CONDITIONS}; +use crate::gen::flags::{ + DONT_VALIDATE_SIGNATURE, ENABLE_SHA256TREE_CONDITIONS, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT, +}; use crate::gen::make_aggsig_final_message::u64_to_bytes; use crate::gen::messages::{Message, SpendId}; use crate::gen::spend_visitor::SpendVisitor; @@ -1267,7 +1269,7 @@ pub fn parse_conditions( assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); } Condition::AssertSha256Tree(sexp, hash) => { - if flags & ENABLE_SHA256TREE_CONDITIONS == 1{ + if flags & ENABLE_SHA256TREE_CONDITIONS == 1 { // for now we can validate this here // in the future we might want to store the jobs and validate them more efficiently if clvm_utils::tree_hash(&a, sexp).to_bytes() != a.atom(hash).as_ref() { @@ -1866,6 +1868,11 @@ fn test_invalid_condition_list2() { ); } +// #[test] +// fn test_shatree_condition() { +// cond_test("()") +// } + #[test] fn test_invalid_condition_args_terminator() { // we only look at the condition arguments the condition expects, any @@ -2370,12 +2377,13 @@ fn test_multiple_conditions( #[case(AGG_SIG_PARENT_AMOUNT)] #[case(ASSERT_CONCURRENT_SPEND)] #[case(ASSERT_CONCURRENT_PUZZLE)] +#[case(ASSERT_SHA256_TREE)] fn test_missing_arg(#[case] condition: ConditionOpcode) { // extra args are disallowed in mempool mode assert_eq!( cond_test_flag( &format!("((({{h1}} ({{h2}} (123 ((({} )))))", condition as u8), - 0 + ENABLE_SHA256TREE_CONDITIONS ) .unwrap_err() .1, From e14cb4143b5b2c1a231673cbb4bd4dfe0637af87 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Wed, 12 Feb 2025 14:36:52 +0000 Subject: [PATCH 3/8] first test passing, more work to do --- crates/chia-consensus/src/gen/conditions.rs | 31 ++++++++++++++----- crates/chia-consensus/src/gen/opcodes.rs | 3 +- .../src/gen/validation_error.rs | 2 ++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 514dfe2e4..fb4ed8a07 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -587,9 +587,13 @@ pub fn parse_args( Ok(Condition::Skip) } ASSERT_SHA256_TREE => { - maybe_check_args_terminator(a, c, flags)?; + if flags & ENABLE_SHA256TREE_CONDITIONS == 0 { + return Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)); + } + // maybe_check_args_terminator(a, c, flags)?; let sexp = first(a, c)?; - let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertConcurrentPuzzleFailed)?; + c = rest(a, c)?; + let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::InvalidHashValue)?; Ok(Condition::AssertSha256Tree(sexp, id)) } _ => Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)), @@ -1316,7 +1320,7 @@ fn is_ephemeral( // condition op-code pub fn parse_spends( a: &Allocator, - spends: NodePtr, + spends: NodePtr, // list of ((parent_id, puzzle_hash, amount, conditions)... ) max_cost: Cost, flags: u32, aggregate_signature: &Signature, @@ -1868,11 +1872,6 @@ fn test_invalid_condition_list2() { ); } -// #[test] -// fn test_shatree_condition() { -// cond_test("()") -// } - #[test] fn test_invalid_condition_args_terminator() { // we only look at the condition arguments the condition expects, any @@ -1993,6 +1992,7 @@ fn test_invalid_spend_list_terminator() { #[case(AGG_SIG_PARENT_AMOUNT, "{pubkey} ({msg1}")] #[case(ASSERT_CONCURRENT_SPEND, "{coin12}")] #[case(ASSERT_CONCURRENT_PUZZLE, "{h2}")] +#[case(ASSERT_SHA256_TREE, "{h1} ({h2}")] fn test_strict_args_count( #[case] condition: ConditionOpcode, #[case] arg: &str, @@ -3977,6 +3977,21 @@ fn test_concurrent_puzzle_fail() { } } +#[test] +fn test_sha256tree() { + let input = "(\ + (({h1} ({h2} (123 (((91 (0x1000 (0x21df504fc8e0a0c53f8da8728a6ce0b2c6911db03184ee59eda9a6a108b008e4 )))\ + ))"; + let _res = cond_test_cb( + input, + MEMPOOL_MODE | ENABLE_SHA256TREE_CONDITIONS, + None, + &Signature::default(), + None, + ) + .unwrap(); +} + #[test] fn test_assert_concurrent_puzzle_self() { // ASSERT_CONCURRENT_PUZZLE diff --git a/crates/chia-consensus/src/gen/opcodes.rs b/crates/chia-consensus/src/gen/opcodes.rs index 9748cead0..83435d856 100644 --- a/crates/chia-consensus/src/gen/opcodes.rs +++ b/crates/chia-consensus/src/gen/opcodes.rs @@ -157,7 +157,8 @@ pub fn parse_opcode(a: &Allocator, op: NodePtr, _flags: u32) -> Option Some(b0), + | RECEIVE_MESSAGE + | ASSERT_SHA256_TREE => Some(b0), _ => None, } } else { diff --git a/crates/chia-consensus/src/gen/validation_error.rs b/crates/chia-consensus/src/gen/validation_error.rs index 14a520933..47aafe3ab 100644 --- a/crates/chia-consensus/src/gen/validation_error.rs +++ b/crates/chia-consensus/src/gen/validation_error.rs @@ -164,6 +164,7 @@ pub enum ErrorCode { InvalidCoinId, MessageNotSentOrReceived, AssertSha256TreeFailed, + InvalidHashValue, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] @@ -361,6 +362,7 @@ impl From for u32 { ErrorCode::InvalidCoinId => 146, ErrorCode::MessageNotSentOrReceived => 147, ErrorCode::AssertSha256TreeFailed => 148, + ErrorCode::InvalidHashValue => 149, } } } From 90b64a048e41e8afe1471834f077329c4b7b3c21 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Wed, 12 Feb 2025 15:40:34 +0000 Subject: [PATCH 4/8] fix length checking and other tests --- crates/chia-consensus/src/gen/conditions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index fb4ed8a07..947b94b4c 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -590,9 +590,9 @@ pub fn parse_args( if flags & ENABLE_SHA256TREE_CONDITIONS == 0 { return Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)); } - // maybe_check_args_terminator(a, c, flags)?; let sexp = first(a, c)?; c = rest(a, c)?; + maybe_check_args_terminator(a, c, flags)?; let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::InvalidHashValue)?; Ok(Condition::AssertSha256Tree(sexp, id)) } @@ -2004,7 +2004,7 @@ fn test_strict_args_count( "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 )))))", condition as u8, arg ), - flags | DONT_VALIDATE_SIGNATURE, + flags | DONT_VALIDATE_SIGNATURE | ENABLE_SHA256TREE_CONDITIONS, ); if flags == 0 { // two of the cases won't pass, even when garbage at the end is allowed. From f24d1d046c006c997aa67a6e8dc1342c58944011 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Wed, 12 Feb 2025 16:08:12 +0000 Subject: [PATCH 5/8] clippy fixes and test fix --- crates/chia-consensus/src/gen/conditions.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 947b94b4c..2e88b54f2 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -1273,10 +1273,10 @@ pub fn parse_conditions( assert_not_ephemeral(&mut spend.flags, state, ret.spends.len()); } Condition::AssertSha256Tree(sexp, hash) => { - if flags & ENABLE_SHA256TREE_CONDITIONS == 1 { + if flags & ENABLE_SHA256TREE_CONDITIONS != 0 { // for now we can validate this here // in the future we might want to store the jobs and validate them more efficiently - if clvm_utils::tree_hash(&a, sexp).to_bytes() != a.atom(hash).as_ref() { + if clvm_utils::tree_hash(a, sexp).to_bytes() != a.atom(hash).as_ref() { return Err(ValidationErr(c, ErrorCode::AssertSha256TreeFailed)); } } @@ -2007,7 +2007,7 @@ fn test_strict_args_count( flags | DONT_VALIDATE_SIGNATURE | ENABLE_SHA256TREE_CONDITIONS, ); if flags == 0 { - // two of the cases won't pass, even when garbage at the end is allowed. + // three of the cases won't pass, even when garbage at the end is allowed. if condition == ASSERT_COIN_ANNOUNCEMENT { assert_eq!(ret.unwrap_err().1, ErrorCode::AssertCoinAnnouncementFailed,); } else if condition == ASSERT_PUZZLE_ANNOUNCEMENT { @@ -2015,6 +2015,8 @@ fn test_strict_args_count( ret.unwrap_err().1, ErrorCode::AssertPuzzleAnnouncementFailed, ); + } else if condition == ASSERT_SHA256_TREE { + assert_eq!(ret.unwrap_err().1, ErrorCode::AssertSha256TreeFailed,); } else { assert!(ret.is_ok()); } From 218be2bedeb294b2bc13d2e20c2802a9996facf7 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Wed, 12 Feb 2025 17:20:22 +0000 Subject: [PATCH 6/8] add debug print for assertsha256tree --- crates/chia-tools/src/bin/run-spend.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/chia-tools/src/bin/run-spend.rs b/crates/chia-tools/src/bin/run-spend.rs index dddcc19ad..eebf2ab45 100644 --- a/crates/chia-tools/src/bin/run-spend.rs +++ b/crates/chia-tools/src/bin/run-spend.rs @@ -127,6 +127,13 @@ impl DebugPrint for Condition { } Self::Skip => "[Skip] REMARK ...".to_string(), Self::SkipRelativeCondition => "[SkipRelativeCondition]".to_string(), + Self::AssertSha256Tree(sexp, hash) => { + format!( + "ASSERT_SHA256_TREE {} {}", + sexp.debug_print(a), + hash.debug_print(a) + ) + } } } } From 3a65ae51eb849f79bcd4f7dd83977e0a80410053 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Wed, 12 Feb 2025 17:59:48 +0000 Subject: [PATCH 7/8] reformat test to allow cases and add cases for cons box and list --- crates/chia-consensus/src/gen/conditions.rs | 62 ++++++++++++++++----- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 2e88b54f2..3b76d89b5 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -3979,19 +3979,55 @@ fn test_concurrent_puzzle_fail() { } } -#[test] -fn test_sha256tree() { - let input = "(\ - (({h1} ({h2} (123 (((91 (0x1000 (0x21df504fc8e0a0c53f8da8728a6ce0b2c6911db03184ee59eda9a6a108b008e4 )))\ - ))"; - let _res = cond_test_cb( - input, - MEMPOOL_MODE | ENABLE_SHA256TREE_CONDITIONS, - None, - &Signature::default(), - None, - ) - .unwrap(); +#[cfg(test)] +#[rstest] +#[case( + "0x1000", + "0x21df504fc8e0a0c53f8da8728a6ce0b2c6911db03184ee59eda9a6a108b008e4", + true +)] +#[case( + "0x1000", + "0x21df504fc8e0a0c53f8da8728a6ce0b2c6911db03184ee59eda9a6a108b008e5", + false +)] +#[case( + "(0x1000 0x2000 ", + "0x52beefa879dfaab96e4e42d202da06853e0f64f07e5144fcb1e46cb2de3eb7dc", + true +)] // (0x1000 . 0x2000) +#[case( + "(0x1000 0x2000 ", + "0x52beefa879dfaab96e4e42d202da06853e0f64f07e5144fcb1e46cb2de3eb7dd", + false +)] // (0x1000 . 0x2000) +#[case( + "(0x1000 (0x2000 (0x3000 ) ", + "0x02fa79e6a347b47ddd95a785b045de89f87d9c0f0cafec012d127ae4ebc1dc9b", + true +)] // (0x1000 0x2000 0x300) +#[case( + "(0x1000 (0x2000 (0x3000 ) ", + "0x02fa79e6a347b47ddd95a785b045de89f87d9c0f0cafec012d127ae4ebc1dc9c", + false +)] // (0x1000 0x2000 0x300) +fn test_sha256tree(#[case] sexp: &str, #[case] hash: &str, #[case] expected: bool) { + let input = format!( + "(\ + (({{h1}} ({{h2}} (123 (((91 ({sexp} ({hash} )))\ + ))" + ); + assert_eq!( + cond_test_cb( + &input, + MEMPOOL_MODE | ENABLE_SHA256TREE_CONDITIONS, + None, + &Signature::default(), + None, + ) + .is_ok(), + expected + ); } #[test] From afaee3e9ce02e639e67c4824eb47f37036a7fdd1 Mon Sep 17 00:00:00 2001 From: Matthew Howard Date: Mon, 17 Feb 2025 12:50:19 +0000 Subject: [PATCH 8/8] refactor ready for cache --- crates/chia-consensus/src/gen/conditions.rs | 29 ++++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 3b76d89b5..c39a1c129 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -239,7 +239,7 @@ pub enum Condition { SkipRelativeCondition, // assert sha256tree (SExp, 32 bytes) - AssertSha256Tree(NodePtr, NodePtr), + AssertSha256Tree(NodePtr, [u8; 32]), } fn check_agg_sig_unsafe_message( @@ -594,7 +594,12 @@ pub fn parse_args( c = rest(a, c)?; maybe_check_args_terminator(a, c, flags)?; let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::InvalidHashValue)?; - Ok(Condition::AssertSha256Tree(sexp, id)) + let hash: [u8; 32] = a + .atom(id) + .as_ref() + .try_into() + .expect("we already sanitised this"); + Ok(Condition::AssertSha256Tree(sexp, hash)) } _ => Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)), } @@ -807,6 +812,9 @@ pub struct ParseState { // TODO: We would probably save heap allocations by turning this into a // blst_pairing object. pub pkm_pairs: Vec<(PublicKey, Bytes)>, + + // shatree asserts + sha256tree_asserts: Vec<(NodePtr, [u8; 32])>, } // returns (parent-id, puzzle-hash, amount, condition-list) @@ -1274,11 +1282,11 @@ pub fn parse_conditions( } Condition::AssertSha256Tree(sexp, hash) => { if flags & ENABLE_SHA256TREE_CONDITIONS != 0 { - // for now we can validate this here - // in the future we might want to store the jobs and validate them more efficiently - if clvm_utils::tree_hash(a, sexp).to_bytes() != a.atom(hash).as_ref() { - return Err(ValidationErr(c, ErrorCode::AssertSha256TreeFailed)); - } + // the raison d'etre for this condition is to pay extra for guaranteed caching, to make it cheaper overall + // if clvm_utils::tree_hash(a, sexp).to_bytes() != a.atom(hash).as_ref() { + // return Err(ValidationErr(c, ErrorCode::AssertSha256TreeFailed)); + // } + state.sha256tree_asserts.push((sexp, hash)); } } Condition::Skip => {} @@ -1483,6 +1491,13 @@ pub fn validate_conditions( } } + for (sexp, hash) in &state.sha256tree_asserts { + // TODO: add caching here + if clvm_utils::tree_hash(a, *sexp).to_bytes() != *hash { + return Err(ValidationErr(*sexp, ErrorCode::AssertSha256TreeFailed)); + } + } + if !state.assert_puzzle.is_empty() { let mut announcements = HashSet::::new();