From 94a333d161f13dba51d8aef5db3b18b74a9d3d4c Mon Sep 17 00:00:00 2001
From: arvidn <arvid@libtorrent.org>
Date: Thu, 3 Oct 2024 16:48:26 +0200
Subject: [PATCH 1/6] validate block signature as part of
 run_block_generator(). There is an opt-out for tests and maybe we'll need it
 in production too

---
 Cargo.lock                                    |   1 +
 crates/chia-bls/src/bls_cache.rs              |   7 +
 .../chia-consensus/benches/run-generator.rs   |   9 +-
 crates/chia-consensus/fuzz/Cargo.toml         |   1 +
 .../fuzz/fuzz_targets/parse-spends.rs         |  12 +-
 .../fuzz/fuzz_targets/run-generator.rs        |   5 +
 crates/chia-consensus/src/fast_forward.rs     |   3 +-
 crates/chia-consensus/src/gen/conditions.rs   | 381 ++++++++++++++++--
 crates/chia-consensus/src/gen/flags.rs        |   6 +
 .../src/gen/get_puzzle_and_solution.rs        |   7 +-
 .../src/gen/owned_conditions.rs               |   4 +
 .../src/gen/run_block_generator.rs            |  26 +-
 .../chia-consensus/src/gen/test_generators.rs |  11 +-
 .../src/spendbundle_conditions.rs             |  38 +-
 .../src/spendbundle_validation.rs             |  57 +--
 crates/chia-tools/src/bin/analyze-chain.rs    |   2 +
 .../src/bin/test-block-generators.rs          |  16 +-
 tests/run_gen.py                              |  12 +-
 tests/test_get_puzzle_and_solution.py         |  10 +-
 tests/test_run_block_generator.py             |  55 ++-
 tests/test_streamable.py                      |  12 +-
 wheel/generate_type_stubs.py                  |   6 +-
 wheel/python/chia_rs/chia_rs.pyi              |  12 +-
 wheel/src/api.rs                              |   3 +-
 wheel/src/run_generator.rs                    |  33 +-
 25 files changed, 600 insertions(+), 129 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a1dea13b0..e963b49bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -373,6 +373,7 @@ dependencies = [
 name = "chia-fuzz"
 version = "0.13.0"
 dependencies = [
+ "chia-bls 0.14.1",
  "chia-consensus",
  "chia-protocol",
  "chia-sha2",
diff --git a/crates/chia-bls/src/bls_cache.rs b/crates/chia-bls/src/bls_cache.rs
index f1d401fd7..343d73cf1 100644
--- a/crates/chia-bls/src/bls_cache.rs
+++ b/crates/chia-bls/src/bls_cache.rs
@@ -81,6 +81,13 @@ impl BlsCache {
 
         aggregate_verify_gt(sig, iter)
     }
+
+    pub fn update(&mut self, aug_msg: &[u8], gt: GTElement) {
+        let mut hasher = Sha256::new();
+        hasher.update(aug_msg.as_ref());
+        let hash: [u8; 32] = hasher.finalize();
+        self.cache.put(hash, gt);
+    }
 }
 
 #[cfg(feature = "py-bindings")]
diff --git a/crates/chia-consensus/benches/run-generator.rs b/crates/chia-consensus/benches/run-generator.rs
index 9ee010abf..30a7c74ab 100644
--- a/crates/chia-consensus/benches/run-generator.rs
+++ b/crates/chia-consensus/benches/run-generator.rs
@@ -1,5 +1,6 @@
+use chia_bls::Signature;
 use chia_consensus::consensus_constants::TEST_CONSTANTS;
-use chia_consensus::gen::flags::ALLOW_BACKREFS;
+use chia_consensus::gen::flags::{ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE};
 use chia_consensus::gen::run_block_generator::{run_block_generator, run_block_generator2};
 use clvmr::serde::{node_from_bytes, node_to_bytes_backrefs};
 use clvmr::Allocator;
@@ -55,7 +56,9 @@ fn run(c: &mut Criterion) {
                         gen,
                         &block_refs,
                         11_000_000_000,
-                        ALLOW_BACKREFS,
+                        ALLOW_BACKREFS | DONT_VALIDATE_SIGNATURE,
+                        &Signature::default(),
+                        None,
                         &TEST_CONSTANTS,
                     );
                     let _ = black_box(conds);
@@ -74,6 +77,8 @@ fn run(c: &mut Criterion) {
                         &block_refs,
                         11_000_000_000,
                         ALLOW_BACKREFS,
+                        &Signature::default(),
+                        None,
                         &TEST_CONSTANTS,
                     );
                     let _ = black_box(conds);
diff --git a/crates/chia-consensus/fuzz/Cargo.toml b/crates/chia-consensus/fuzz/Cargo.toml
index 2bc426fcd..ac2dc6092 100644
--- a/crates/chia-consensus/fuzz/Cargo.toml
+++ b/crates/chia-consensus/fuzz/Cargo.toml
@@ -20,6 +20,7 @@ chia-protocol = { workspace = true }
 chia-sha2 = { workspace = true }
 chia-traits = { workspace = true }
 chia-consensus = { workspace = true }
+chia-bls = { workspace = true }
 hex-literal = { workspace = true }
 
 [[bin]]
diff --git a/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs b/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs
index 84257c734..7e9dbdcf4 100644
--- a/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs
+++ b/crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs
@@ -1,6 +1,7 @@
 #![no_main]
 use libfuzzer_sys::fuzz_target;
 
+use chia_bls::Signature;
 use chia_consensus::gen::conditions::{parse_spends, MempoolVisitor};
 use chia_fuzz::{make_list, BitCursor};
 use clvmr::{Allocator, NodePtr};
@@ -14,7 +15,14 @@ fuzz_target!(|data: &[u8]| {
     // 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] {
-        let _ret =
-            parse_spends::<MempoolVisitor>(&a, input, 33_000_000_000, *flags, &TEST_CONSTANTS);
+        let _ret = parse_spends::<MempoolVisitor>(
+            &a,
+            input,
+            33_000_000_000,
+            *flags,
+            &Signature::default(),
+            None,
+            &TEST_CONSTANTS,
+        );
     }
 });
diff --git a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs
index b34c03811..ee7118e0a 100644
--- a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs
+++ b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs
@@ -1,4 +1,5 @@
 #![no_main]
+use chia_bls::Signature;
 use chia_consensus::allocator::make_allocator;
 use chia_consensus::consensus_constants::TEST_CONSTANTS;
 use chia_consensus::gen::flags::ALLOW_BACKREFS;
@@ -15,6 +16,8 @@ fuzz_target!(|data: &[u8]| {
         [],
         110_000_000,
         ALLOW_BACKREFS,
+        &Signature::default(),
+        None,
         &TEST_CONSTANTS,
     );
     drop(a1);
@@ -26,6 +29,8 @@ fuzz_target!(|data: &[u8]| {
         [],
         110_000_000,
         ALLOW_BACKREFS,
+        &Signature::default(),
+        None,
         &TEST_CONSTANTS,
     );
     drop(a2);
diff --git a/crates/chia-consensus/src/fast_forward.rs b/crates/chia-consensus/src/fast_forward.rs
index a803a5a21..f3195a4c6 100644
--- a/crates/chia-consensus/src/fast_forward.rs
+++ b/crates/chia-consensus/src/fast_forward.rs
@@ -155,7 +155,6 @@ pub fn fast_forward_singleton(
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::consensus_constants::ConsensusConstants;
     use crate::consensus_constants::TEST_CONSTANTS;
     use crate::gen::conditions::MempoolVisitor;
     use crate::gen::conditions::{
@@ -173,7 +172,7 @@ mod tests {
     use clvmr::chia_dialect::ChiaDialect;
     use clvmr::reduction::Reduction;
     use clvmr::run_program::run_program;
-    use clvmr::serde::{node_from_bytes, node_from_bytes_backrefs, node_to_bytes};
+    use clvmr::serde::{node_from_bytes, node_to_bytes};
     use hex_literal::hex;
     use rstest::rstest;
     use std::fs;
diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs
index e2a491ee3..a15b23169 100644
--- a/crates/chia-consensus/src/gen/conditions.rs
+++ b/crates/chia-consensus/src/gen/conditions.rs
@@ -17,11 +17,12 @@ use super::opcodes::{
 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::{NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT};
+use crate::gen::flags::{DONT_VALIDATE_SIGNATURE, 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;
 use crate::gen::validation_error::check_nil;
-use chia_bls::PublicKey;
+use chia_bls::{aggregate_verify, BlsCache, PublicKey, Signature};
 use chia_protocol::Bytes32;
 use chia_sha2::Sha256;
 use clvmr::allocator::{Allocator, NodePtr, SExp};
@@ -719,6 +720,9 @@ pub struct SpendBundleConditions {
 
     // the sum of all amounts of CREATE_COIN conditions
     pub addition_amount: u128,
+
+    // true if the block/spend bundle aggregate signature was validated
+    pub validated_signature: bool,
 }
 
 #[derive(Default)]
@@ -775,6 +779,13 @@ pub struct ParseState {
     // ASSERT_MY_BIRTH_HEIGHT
     // each item is the index into the SpendBundleConditions::spends vector
     assert_not_ephemeral: HashSet<usize>,
+
+    // All public keys and messages emitted by the generator. We'll validate
+    // these against the aggregate signature at the end, unless the
+    // DONT_VALIDATE_SIGNATURE flag is set
+    // TODO: We would probably save heap allocations by turning this into a
+    // blst_pairing object.
+    pub pkm_pairs: Vec<(PublicKey, Vec<u8>)>,
 }
 
 // returns (parent-id, puzzle-hash, amount, condition-list)
@@ -1121,30 +1132,80 @@ pub fn parse_conditions<V: SpendVisitor>(
             }
             Condition::AggSigMe(pk, msg) => {
                 spend.agg_sig_me.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend((*spend.coin_id).as_slice());
+                    msg.extend(constants.agg_sig_me_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigParent(pk, msg) => {
                 spend.agg_sig_parent.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(a.atom(spend.parent_id).as_ref());
+                    msg.extend(constants.agg_sig_parent_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigPuzzle(pk, msg) => {
                 spend.agg_sig_puzzle.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(a.atom(spend.puzzle_hash).as_ref());
+                    msg.extend(constants.agg_sig_puzzle_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigAmount(pk, msg) => {
                 spend.agg_sig_amount.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
+                    msg.extend(constants.agg_sig_amount_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigPuzzleAmount(pk, msg) => {
                 spend.agg_sig_puzzle_amount.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(a.atom(spend.puzzle_hash).as_ref());
+                    msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
+                    msg.extend(constants.agg_sig_puzzle_amount_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigParentAmount(pk, msg) => {
                 spend.agg_sig_parent_amount.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(a.atom(spend.parent_id).as_ref());
+                    msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
+                    msg.extend(constants.agg_sig_parent_amount_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigParentPuzzle(pk, msg) => {
                 spend.agg_sig_parent_puzzle.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    let mut msg = a.atom(msg).as_ref().to_vec();
+                    msg.extend(a.atom(spend.parent_id).as_ref());
+                    msg.extend(a.atom(spend.puzzle_hash).as_ref());
+                    msg.extend(constants.agg_sig_parent_puzzle_additional_data.as_slice());
+                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                }
             }
             Condition::AggSigUnsafe(pk, msg) => {
-                // AGG_SIG_UNSAFE messages are not allowed to end with the
-                // suffix added to other AGG_SIG_* conditions
-                check_agg_sig_unsafe_message(a, msg, constants)?;
                 ret.agg_sig_unsafe.push((to_key(a, pk)?, msg));
+                if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
+                    // AGG_SIG_UNSAFE messages are not allowed to end with the
+                    // suffix added to other AGG_SIG_* conditions
+                    check_agg_sig_unsafe_message(a, msg, constants)?;
+                    state
+                        .pkm_pairs
+                        .push((to_key(a, pk)?, a.atom(msg).as_ref().to_vec()));
+                }
             }
             Condition::Softfork(cost) => {
                 if *max_cost < cost {
@@ -1229,6 +1290,8 @@ pub fn parse_spends<V: SpendVisitor>(
     spends: NodePtr,
     max_cost: Cost,
     flags: u32,
+    aggregate_signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
     constants: &ConsensusConstants,
 ) -> Result<SpendBundleConditions, ValidationErr> {
     let mut ret = SpendBundleConditions::default();
@@ -1261,8 +1324,10 @@ pub fn parse_spends<V: SpendVisitor>(
     }
 
     validate_conditions(a, &ret, &state, spends, flags)?;
-    ret.cost = max_cost - cost_left;
+    validate_signature(&state, aggregate_signature, flags, bls_cache)?;
+    ret.validated_signature = (flags & DONT_VALIDATE_SIGNATURE) == 0;
 
+    ret.cost = max_cost - cost_left;
     Ok(ret)
 }
 
@@ -1434,6 +1499,38 @@ pub fn validate_conditions(
     Ok(())
 }
 
+pub fn validate_signature(
+    state: &ParseState,
+    signature: &Signature,
+    flags: u32,
+    bls_cache: Option<&mut BlsCache>,
+) -> Result<(), ValidationErr> {
+    if (flags & DONT_VALIDATE_SIGNATURE) != 0 {
+        return Ok(());
+    }
+
+    if let Some(bls_cache) = bls_cache {
+        if !bls_cache.aggregate_verify(
+            state.pkm_pairs.iter().map(|(pk, msg)| (pk, msg.as_slice())),
+            signature,
+        ) {
+            return Err(ValidationErr(
+                NodePtr::NIL,
+                ErrorCode::BadAggregateSignature,
+            ));
+        }
+    } else if !aggregate_verify(
+        signature,
+        state.pkm_pairs.iter().map(|(pk, msg)| (pk, msg.as_slice())),
+    ) {
+        return Err(ValidationErr(
+            NodePtr::NIL,
+            ErrorCode::BadAggregateSignature,
+        ));
+    }
+    Ok(())
+}
+
 #[cfg(test)]
 use crate::consensus_constants::TEST_CONSTANTS;
 #[cfg(test)]
@@ -1465,7 +1562,10 @@ const LONG_VEC: &[u8; 33] = &[
 ];
 
 #[cfg(test)]
-const PUBKEY: &[u8; 48] = &hex!("97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb");
+const PUBKEY: &[u8; 48] = &hex!("aefe1789d6476f60439e1168f588ea16652dc321279f05a805fbc63933e88ae9c175d6c6ab182e54af562e1a0dce41bb");
+#[cfg(test)]
+const SECRET_KEY: &[u8; 32] =
+    &hex!("6fc9d9a2b05fd1f0e51bc91041a03be8657081f272ec281aff731624f0d1c220");
 #[cfg(test)]
 const MSG1: &[u8; 13] = &[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3];
 #[cfg(test)]
@@ -1508,9 +1608,6 @@ const LONGMSG: &[u8; 1025] = &[
     4,
 ];
 
-#[cfg(test)]
-use crate::gen::make_aggsig_final_message::u64_to_bytes;
-
 #[cfg(test)]
 fn hash_buf(b1: &[u8], b2: &[u8]) -> Vec<u8> {
     let mut ctx = Sha256::new();
@@ -1668,6 +1765,8 @@ fn cond_test_cb(
     input: &str,
     flags: u32,
     callback: Callback,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
 ) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
     let mut a = Allocator::new();
 
@@ -1678,7 +1777,15 @@ fn cond_test_cb(
         print!("{c:02x}");
     }
     println!();
-    match parse_spends::<MempoolVisitor>(&a, n, 11_000_000_000, flags, &TEST_CONSTANTS) {
+    match parse_spends::<MempoolVisitor>(
+        &a,
+        n,
+        11_000_000_000,
+        flags,
+        signature,
+        bls_cache,
+        &TEST_CONSTANTS,
+    ) {
         Ok(list) => {
             for n in &list.spends {
                 println!("{n:?}");
@@ -1695,7 +1802,7 @@ use crate::gen::flags::MEMPOOL_MODE;
 #[cfg(test)]
 fn cond_test(input: &str) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
     // by default, run all tests in strict mempool mode
-    cond_test_cb(input, MEMPOOL_MODE, None)
+    cond_test_cb(input, MEMPOOL_MODE, None, &Signature::default(), None)
 }
 
 #[cfg(test)]
@@ -1703,7 +1810,17 @@ fn cond_test_flag(
     input: &str,
     flags: u32,
 ) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
-    cond_test_cb(input, flags, None)
+    cond_test_cb(input, flags, None, &Signature::default(), None)
+}
+
+#[cfg(test)]
+fn cond_test_sig(
+    input: &str,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
+    flags: u32,
+) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
+    cond_test_cb(input, flags, None, signature, bls_cache)
 }
 
 #[test]
@@ -1853,7 +1970,7 @@ fn test_strict_args_count(
             "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 )))))",
             condition as u8, arg
         ),
-        flags,
+        flags | DONT_VALIDATE_SIGNATURE,
     );
     if flags == 0 {
         // two of the cases won't pass, even when garbage at the end is allowed.
@@ -1898,7 +2015,7 @@ fn test_message_strict_args_count(
         &format!(
             "((({{h1}} ({{h2}} (123 (((66 ({mode} ({msg} {arg} {extra1} ) ((67 ({mode} ({msg} {extra2} ) ))))"
         ),
-        flags,
+        flags | DONT_VALIDATE_SIGNATURE,
     );
     if flags == 0 {
         ret.unwrap();
@@ -1942,14 +2059,26 @@ fn test_extra_arg(
     #[case] extra_cond: &str,
     #[case] test: impl Fn(&SpendBundleConditions, &SpendConditions),
 ) {
+    let signature = match condition {
+        AGG_SIG_PARENT
+        | AGG_SIG_PUZZLE
+        | AGG_SIG_AMOUNT
+        | AGG_SIG_PUZZLE_AMOUNT
+        | AGG_SIG_PARENT_PUZZLE
+        | AGG_SIG_PARENT_AMOUNT => sign_tx(H1, H2, 123, condition, MSG1),
+        _ => Signature::default(),
+    };
+
     // extra args are ignored in consensus mode
     // and a failure in mempool mode
     assert_eq!(
-        cond_test_flag(
+        cond_test_sig(
             &format!(
                 "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 ) {} ))))",
                 condition as u8, arg, extra_cond
             ),
+            &signature,
+            None,
             MEMPOOL_MODE,
         )
         .unwrap_err()
@@ -1957,11 +2086,13 @@ fn test_extra_arg(
         ErrorCode::InvalidCondition
     );
 
-    let (a, conds) = cond_test_flag(
+    let (a, conds) = cond_test_sig(
         &format!(
             "((({{h1}} ({{h2}} (123 ((({} ({} ( 1337 ) {} ))))",
             condition as u8, arg, extra_cond
         ),
+        &signature,
+        None,
         0,
     )
     .unwrap();
@@ -2934,7 +3065,9 @@ fn test_create_coin_exceed_cost() {
                     rest = a.new_pair(coin, rest).unwrap();
                 }
                 rest
-            }))
+            })),
+            &Signature::default(),
+            None,
         )
         .unwrap_err()
         .1,
@@ -2993,8 +3126,11 @@ fn test_single_agg_sig_me(
     #[case] condition: ConditionOpcode,
     #[values(MEMPOOL_MODE, 0)] mempool: u32,
 ) {
-    let (a, conds) = cond_test_flag(
+    let signature = sign_tx(H1, H2, 123, condition, MSG1);
+    let (a, conds) = cond_test_sig(
         &format!("((({{h1}} ({{h2}} (123 ((({condition} ({{pubkey}} ({{msg1}} )))))"),
+        &signature,
+        None,
         mempool,
     )
     .unwrap();
@@ -3033,7 +3169,7 @@ fn test_duplicate_agg_sig(
     // aggregated, and so must all copies of the public keys
     let (a, conds) =
         cond_test_flag(&format!("((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{msg1}} ) (({} ({{pubkey}} ({{msg1}} ) ))))", condition as u8, condition as u8),
-            mempool)
+            mempool | DONT_VALIDATE_SIGNATURE)
             .unwrap();
 
     assert_eq!(conds.cost, AGG_SIG_COST * 2);
@@ -3073,7 +3209,7 @@ fn test_agg_sig_invalid_pubkey(
                 "((({{h1}} ({{h2}} (123 ((({} ({{h2}} ({{msg1}} )))))",
                 condition as u8
             ),
-            mempool
+            mempool | DONT_VALIDATE_SIGNATURE
         )
         .unwrap_err()
         .1,
@@ -3166,7 +3302,9 @@ fn test_agg_sig_exceed_cost(#[case] condition: ConditionOpcode) {
                     rest = a.new_pair(aggsig, rest).unwrap();
                 }
                 rest
-            }))
+            })),
+            &Signature::default(),
+            None,
         )
         .unwrap_err()
         .1,
@@ -3177,7 +3315,15 @@ fn test_agg_sig_exceed_cost(#[case] condition: ConditionOpcode) {
 #[test]
 fn test_single_agg_sig_unsafe() {
     // AGG_SIG_UNSAFE
-    let (a, conds) = cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} )))))").unwrap();
+    let signature = sign_tx(H1, H2, 123, 49, MSG1);
+
+    let (a, conds) = cond_test_sig(
+        "((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} )))))",
+        &signature,
+        None,
+        0,
+    )
+    .unwrap();
 
     assert_eq!(conds.cost, AGG_SIG_COST);
     assert_eq!(conds.spends.len(), 1);
@@ -3211,7 +3357,7 @@ fn test_agg_sig_extra_arg(#[case] condition: ConditionOpcode) {
             "((({{h1}} ({{h2}} (123 ((({} ({{pubkey}} ({{msg1}} ( 1337 ) ))))",
             condition as u8
         ),
-        0,
+        DONT_VALIDATE_SIGNATURE,
     )
     .unwrap();
 
@@ -3246,8 +3392,14 @@ fn test_agg_sig_extra_arg(#[case] condition: ConditionOpcode) {
 fn test_agg_sig_unsafe_invalid_terminator() {
     // AGG_SIG_UNSAFE
     // in non-mempool mode, even an invalid terminator is allowed
-    let (a, conds) =
-        cond_test_flag("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} 456 ))))", 0).unwrap();
+    let signature = sign_tx(H1, H2, 123, 49, MSG1);
+    let (a, conds) = cond_test_sig(
+        "((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} 456 ))))",
+        &signature,
+        None,
+        0,
+    )
+    .unwrap();
 
     assert_eq!(conds.cost, AGG_SIG_COST);
     assert_eq!(conds.spends.len(), 1);
@@ -3269,8 +3421,14 @@ fn test_agg_sig_me_invalid_terminator() {
     // AGG_SIG_ME
     // this has an invalid list terminator of the argument list. This is OK
     // according to the original consensus rules
-    let (a, conds) =
-        cond_test_flag("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} 456 ))))", 0).unwrap();
+    let signature = sign_tx(H1, H2, 123, 50, MSG1);
+    let (a, conds) = cond_test_sig(
+        "((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} 456 ))))",
+        &signature,
+        None,
+        0,
+    )
+    .unwrap();
 
     assert_eq!(conds.cost, AGG_SIG_COST);
     assert_eq!(conds.spends.len(), 1);
@@ -3291,9 +3449,15 @@ fn test_agg_sig_me_invalid_terminator() {
 fn test_duplicate_agg_sig_unsafe() {
     // AGG_SIG_UNSAFE
     // these conditions may not be deduplicated
-    let (a, conds) =
-        cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} ) ((49 ({pubkey} ({msg1} ) ))))")
-            .unwrap();
+    let mut signature = sign_tx(H1, H2, 123, 49, MSG1);
+    signature.aggregate(&sign_tx(H1, H2, 123, 49, MSG1));
+    let (a, conds) = cond_test_sig(
+        "((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} ) ((49 ({pubkey} ({msg1} ) ))))",
+        &signature,
+        None,
+        0,
+    )
+    .unwrap();
 
     assert_eq!(conds.cost, AGG_SIG_COST * 2);
     assert_eq!(conds.spends.len(), 1);
@@ -3332,6 +3496,51 @@ fn test_agg_sig_unsafe_long_msg() {
     );
 }
 
+#[cfg(test)]
+fn final_message(
+    parent: &[u8; 32],
+    puzzle: &[u8; 32],
+    amount: u64,
+    opcode: u16,
+    msg: &[u8],
+) -> Vec<u8> {
+    use crate::allocator::make_allocator;
+    use crate::gen::make_aggsig_final_message::make_aggsig_final_message;
+    use crate::gen::owned_conditions::OwnedSpendConditions;
+    use chia_protocol::Coin;
+    use clvmr::LIMIT_HEAP;
+
+    let coin = Coin::new(Bytes32::from(parent), Bytes32::from(puzzle), amount);
+
+    let mut a: Allocator = make_allocator(LIMIT_HEAP);
+    let spend = SpendConditions::new(
+        a.new_atom(parent.as_slice()).expect("should pass"),
+        amount,
+        a.new_atom(puzzle.as_slice()).expect("test should pass"),
+        Arc::new(Bytes32::try_from(coin.coin_id()).expect("test should pass")),
+    );
+
+    let spend = OwnedSpendConditions::from(&a, spend);
+
+    let mut final_msg = msg.to_vec();
+    make_aggsig_final_message(opcode, &mut final_msg, &spend, &TEST_CONSTANTS);
+    final_msg
+}
+
+#[cfg(test)]
+fn sign_tx(
+    parent: &[u8; 32],
+    puzzle: &[u8; 32],
+    amount: u64,
+    opcode: u16,
+    msg: &[u8],
+) -> Signature {
+    use chia_bls::{sign, SecretKey};
+
+    let final_msg = final_message(parent, puzzle, amount, opcode, msg);
+    sign(&SecretKey::from_bytes(SECRET_KEY).unwrap(), final_msg)
+}
+
 #[cfg(test)]
 #[rstest]
 // these are the suffixes used for AGG_SIG_* conditions (other than
@@ -3355,8 +3564,18 @@ fn test_agg_sig_unsafe_invalid_msg(
     #[case] msg: &str,
     #[values(43, 44, 45, 46, 47, 48, 49, 50)] opcode: u16,
 ) {
-    let ret = cond_test_flag(
+    let signature = sign_tx(
+        H1,
+        H2,
+        123,
+        opcode,
+        &hex::decode(&msg[2..]).expect("msg not hex"),
+    );
+
+    let ret = cond_test_sig(
         format!("((({{h1}} ({{h2}} (123 ((({opcode} ({{pubkey}} ({msg} )))))").as_str(),
+        &signature,
+        None,
         0,
     );
     if opcode == AGG_SIG_UNSAFE {
@@ -3394,7 +3613,9 @@ fn test_agg_sig_unsafe_exceed_cost() {
                     rest = a.new_pair(aggsig, rest).unwrap();
                 }
                 rest
-            }))
+            })),
+            &Signature::default(),
+            None,
         )
         .unwrap_err()
         .1,
@@ -4321,6 +4542,8 @@ fn test_limit_announcements(
             }
             rest
         })),
+        &Signature::default(),
+        None,
     );
 
     if expect_err.is_some() {
@@ -4346,7 +4569,7 @@ fn test_eligible_for_ff_assert_parent() {
            ))\
        ))";
 
-    let (_a, cond) = cond_test(test).expect("cond_test");
+    let (_a, cond) = cond_test_flag(test, DONT_VALIDATE_SIGNATURE).expect("cond_test");
     assert!(cond.spends.len() == 1);
     assert!((cond.spends[0].flags & ELIGIBLE_FOR_FF) != 0);
 }
@@ -4441,6 +4664,8 @@ fn test_eligible_for_ff_invalid_agg_sig_me(
     #[case] condition: ConditionOpcode,
     #[case] eligible: bool,
 ) {
+    let signature = sign_tx(H1, H2, 1, condition, MSG1);
+
     // 51=CREATE_COIN
     let test: &str = &format!(
         "(\
@@ -4451,7 +4676,7 @@ fn test_eligible_for_ff_invalid_agg_sig_me(
        ))"
     );
 
-    let (_a, cond) = cond_test_flag(test, 0).expect("cond_test");
+    let (_a, cond) = cond_test_sig(test, &signature, None, 0).expect("cond_test");
     assert!(cond.spends.len() == 1);
     let flags = cond.spends[0].flags;
     if eligible {
@@ -4461,6 +4686,90 @@ fn test_eligible_for_ff_invalid_agg_sig_me(
     }
 }
 
+// test aggregate signature validation. Both positive and negative cases
+
+#[cfg(test)]
+fn add_signature(sig: &mut Signature, puzzle: &mut String, opcode: ConditionOpcode) {
+    if opcode == 0 {
+        return;
+    }
+    sig.aggregate(&sign_tx(H1, H2, 123, opcode, MSG1));
+    puzzle.push_str(format!("(({opcode} ({{pubkey}} ({{msg1}} )").as_str());
+}
+
+#[cfg(test)]
+fn populate_cache(opcode: ConditionOpcode, bls_cache: &mut BlsCache) {
+    use chia_bls::hash_to_g2;
+    let msg = final_message(H1, H2, 123, opcode, MSG1);
+    // Otherwise, we need to calculate the pairing and add it to the cache.
+    let mut aug_msg = PUBKEY.to_vec();
+    aug_msg.extend_from_slice(msg.as_ref());
+    let aug_hash = hash_to_g2(&aug_msg);
+
+    let gt = aug_hash.pair(&PublicKey::from_bytes(PUBKEY).unwrap());
+    bls_cache.update(&aug_msg, gt);
+}
+
+#[cfg(test)]
+#[rstest]
+fn test_agg_sig(
+    #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] a: u32,
+    #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)] b: u32,
+    #[values(true, false)] expect_pass: bool,
+    #[values(true, false)] with_cache: bool,
+) {
+    use chia_bls::{sign, SecretKey};
+    let mut signature = Signature::default();
+    let mut bls_cache = BlsCache::default();
+    let cache: Option<&mut BlsCache> = if with_cache {
+        populate_cache(43, &mut bls_cache);
+        populate_cache(44, &mut bls_cache);
+        populate_cache(45, &mut bls_cache);
+        populate_cache(46, &mut bls_cache);
+        populate_cache(47, &mut bls_cache);
+        populate_cache(48, &mut bls_cache);
+        populate_cache(49, &mut bls_cache);
+        populate_cache(50, &mut bls_cache);
+        Some(&mut bls_cache)
+    } else {
+        None
+    };
+
+    let combination = (a << 4) | b;
+    let mut puzzle: String = "((({h1} ({h2} (123 (".into();
+    let opcodes: &[ConditionOpcode] = &[
+        AGG_SIG_PARENT,
+        AGG_SIG_PUZZLE,
+        AGG_SIG_AMOUNT,
+        AGG_SIG_PUZZLE_AMOUNT,
+        AGG_SIG_PARENT_AMOUNT,
+        AGG_SIG_PARENT_PUZZLE,
+        AGG_SIG_UNSAFE,
+        AGG_SIG_ME,
+    ];
+    for (i, opcode) in opcodes.iter().enumerate() {
+        if (combination & (1 << i)) == 0 {
+            continue;
+        }
+        add_signature(&mut signature, &mut puzzle, *opcode);
+    }
+    puzzle.push_str("))))");
+    if !expect_pass {
+        signature.aggregate(&sign(
+            &SecretKey::from_bytes(SECRET_KEY).unwrap(),
+            b"foobar",
+        ));
+    }
+    match cond_test_sig(puzzle.as_str(), &signature, cache, 0) {
+        Ok(..) => {
+            assert!(expect_pass);
+        }
+        Err(..) => {
+            assert!(!expect_pass);
+        }
+    }
+}
+
 // the message condition takes a mode-parameter. This is a 6-bit integer that
 // determines which aspects of the sending spend and receiving spends must
 // match. The second argument is the message. The message and mode must always
@@ -4697,6 +5006,8 @@ fn test_limit_messages(#[case] count: i32, #[case] expect_err: Option<ErrorCode>
             }
             rest
         })),
+        &Signature::default(),
+        None,
     );
 
     if expect_err.is_some() {
diff --git a/crates/chia-consensus/src/gen/flags.rs b/crates/chia-consensus/src/gen/flags.rs
index 830e4d50d..e5991e355 100644
--- a/crates/chia-consensus/src/gen/flags.rs
+++ b/crates/chia-consensus/src/gen/flags.rs
@@ -16,4 +16,10 @@ pub const STRICT_ARGS_COUNT: u32 = 0x8_0000;
 // contain back-references
 pub const ALLOW_BACKREFS: u32 = 0x0200_0000;
 
+// By default, run_block_generator validates the signatures of any AGG_SIG
+// condition. By passing in this flag, the signatures are not validated (saving
+// time). This is useful when we've already validated a block but we need to
+// re-run it to compute additions and removals.
+pub const DONT_VALIDATE_SIGNATURE: u32 = 0x1_0000;
+
 pub const MEMPOOL_MODE: u32 = CLVM_MEMPOOL_MODE | NO_UNKNOWN_CONDS | STRICT_ARGS_COUNT;
diff --git a/crates/chia-consensus/src/gen/get_puzzle_and_solution.rs b/crates/chia-consensus/src/gen/get_puzzle_and_solution.rs
index 53c1e839a..b5b70f45f 100644
--- a/crates/chia-consensus/src/gen/get_puzzle_and_solution.rs
+++ b/crates/chia-consensus/src/gen/get_puzzle_and_solution.rs
@@ -61,9 +61,10 @@ pub fn get_puzzle_and_solution_for_coin(
 mod test {
     use super::*;
     use crate::consensus_constants::TEST_CONSTANTS;
-    use crate::gen::flags::{ALLOW_BACKREFS, MEMPOOL_MODE};
+    use crate::gen::flags::{ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE};
     use crate::gen::make_aggsig_final_message::u64_to_bytes;
     use crate::gen::run_block_generator::{run_block_generator2, setup_generator_args};
+    use chia_bls::Signature;
     use chia_protocol::Bytes32;
     use chia_sha2::Sha256;
     use clvm_traits::FromClvm;
@@ -239,7 +240,9 @@ mod test {
             &generator,
             blocks,
             MAX_COST,
-            ALLOW_BACKREFS | MEMPOOL_MODE,
+            ALLOW_BACKREFS | MEMPOOL_MODE | DONT_VALIDATE_SIGNATURE,
+            &Signature::default(),
+            None,
             &TEST_CONSTANTS,
         )
         .expect("run_block_generator2");
diff --git a/crates/chia-consensus/src/gen/owned_conditions.rs b/crates/chia-consensus/src/gen/owned_conditions.rs
index 491d71d54..3ce68a3d0 100644
--- a/crates/chia-consensus/src/gen/owned_conditions.rs
+++ b/crates/chia-consensus/src/gen/owned_conditions.rs
@@ -67,6 +67,9 @@ pub struct OwnedSpendBundleConditions {
     pub removal_amount: u128,
     // the sum of all amounts of CREATE_COIN conditions
     pub addition_amount: u128,
+    // set if the aggregate signature of the block/spend bundle were
+    // successfully validated
+    pub validated_signature: bool,
 }
 
 impl OwnedSpendConditions {
@@ -140,6 +143,7 @@ impl OwnedSpendBundleConditions {
             cost: sb.cost,
             removal_amount: sb.removal_amount,
             addition_amount: sb.addition_amount,
+            validated_signature: sb.validated_signature,
         }
     }
 }
diff --git a/crates/chia-consensus/src/gen/run_block_generator.rs b/crates/chia-consensus/src/gen/run_block_generator.rs
index af767294f..55c4b47b4 100644
--- a/crates/chia-consensus/src/gen/run_block_generator.rs
+++ b/crates/chia-consensus/src/gen/run_block_generator.rs
@@ -1,11 +1,12 @@
 use crate::consensus_constants::ConsensusConstants;
 use crate::gen::conditions::{
-    parse_spends, process_single_spend, validate_conditions, EmptyVisitor, ParseState,
-    SpendBundleConditions,
+    parse_spends, process_single_spend, validate_conditions, validate_signature, EmptyVisitor,
+    ParseState, SpendBundleConditions,
 };
-use crate::gen::flags::ALLOW_BACKREFS;
+use crate::gen::flags::{ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE};
 use crate::gen::validation_error::{first, ErrorCode, ValidationErr};
 use crate::generator_rom::{CLVM_DESERIALIZER, GENERATOR_ROM};
+use chia_bls::{BlsCache, Signature};
 use clvm_utils::{tree_hash_cached, TreeHash};
 use clvmr::allocator::{Allocator, NodePtr};
 use clvmr::chia_dialect::ChiaDialect;
@@ -67,12 +68,15 @@ where
 // the only reason we need to pass in the allocator is because the returned
 // SpendBundleConditions contains NodePtr fields. If that's changed, we could
 // create the allocator inside this functions as well.
+#[allow(clippy::too_many_arguments)]
 pub fn run_block_generator<GenBuf: AsRef<[u8]>, I: IntoIterator<Item = GenBuf>>(
     a: &mut Allocator,
     program: &[u8],
     block_refs: I,
     max_cost: u64,
     flags: u32,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
     constants: &ConsensusConstants,
 ) -> Result<SpendBundleConditions, ValidationErr>
 where
@@ -112,8 +116,15 @@ where
 
     // we pass in what's left of max_cost here, to fail early in case the
     // cost of a condition brings us over the cost limit
-    let mut result =
-        parse_spends::<EmptyVisitor>(a, generator_output, cost_left, flags, constants)?;
+    let mut result = parse_spends::<EmptyVisitor>(
+        a,
+        generator_output,
+        cost_left,
+        flags,
+        signature,
+        bls_cache,
+        constants,
+    )?;
     result.cost += max_cost - cost_left;
     Ok(result)
 }
@@ -147,12 +158,15 @@ pub fn extract_n<const N: usize>(
 // you only pay cost for the generator, the puzzles and the conditions).
 // it also does not apply the stack depth or object allocation limits the same,
 // as each puzzle run in its own environment.
+#[allow(clippy::too_many_arguments)]
 pub fn run_block_generator2<GenBuf: AsRef<[u8]>, I: IntoIterator<Item = GenBuf>>(
     a: &mut Allocator,
     program: &[u8],
     block_refs: I,
     max_cost: u64,
     flags: u32,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
     constants: &ConsensusConstants,
 ) -> Result<SpendBundleConditions, ValidationErr>
 where
@@ -217,6 +231,8 @@ where
     }
 
     validate_conditions(a, &ret, &state, a.nil(), flags)?;
+    validate_signature(&state, signature, flags, bls_cache)?;
+    ret.validated_signature = (flags & DONT_VALIDATE_SIGNATURE) == 0;
 
     ret.cost = max_cost - cost_left;
     Ok(ret)
diff --git a/crates/chia-consensus/src/gen/test_generators.rs b/crates/chia-consensus/src/gen/test_generators.rs
index ad3ee3388..1366e3ca5 100644
--- a/crates/chia-consensus/src/gen/test_generators.rs
+++ b/crates/chia-consensus/src/gen/test_generators.rs
@@ -2,7 +2,8 @@ use super::conditions::{NewCoin, SpendBundleConditions, SpendConditions};
 use super::run_block_generator::{run_block_generator, run_block_generator2};
 use crate::allocator::make_allocator;
 use crate::consensus_constants::TEST_CONSTANTS;
-use crate::gen::flags::{ALLOW_BACKREFS, MEMPOOL_MODE};
+use crate::gen::flags::{ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE};
+use chia_bls::Signature;
 use chia_protocol::{Bytes, Bytes48};
 use clvmr::allocator::NodePtr;
 use clvmr::Allocator;
@@ -236,7 +237,9 @@ fn run_generator(#[case] name: &str) {
             &generator,
             &block_refs,
             11_000_000_000,
-            *flags,
+            *flags | DONT_VALIDATE_SIGNATURE,
+            &Signature::default(),
+            None,
             &TEST_CONSTANTS,
         );
 
@@ -251,7 +254,9 @@ fn run_generator(#[case] name: &str) {
             &generator,
             &block_refs,
             11_000_000_000,
-            *flags,
+            *flags | DONT_VALIDATE_SIGNATURE,
+            &Signature::default(),
+            None,
             &TEST_CONSTANTS,
         );
         let output_hard_fork = match conds {
diff --git a/crates/chia-consensus/src/spendbundle_conditions.rs b/crates/chia-consensus/src/spendbundle_conditions.rs
index c033ce5ba..22f99e53f 100644
--- a/crates/chia-consensus/src/spendbundle_conditions.rs
+++ b/crates/chia-consensus/src/spendbundle_conditions.rs
@@ -2,11 +2,12 @@ use crate::consensus_constants::ConsensusConstants;
 use crate::gen::conditions::{
     process_single_spend, validate_conditions, MempoolVisitor, ParseState, SpendBundleConditions,
 };
-use crate::gen::flags::MEMPOOL_MODE;
+use crate::gen::flags::{DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE};
 use crate::gen::run_block_generator::subtract_cost;
 use crate::gen::solution_generator::calculate_generator_length;
 use crate::gen::validation_error::ValidationErr;
 use crate::spendbundle_validation::get_flags_for_height_and_constants;
+use chia_bls::PublicKey;
 use chia_protocol::SpendBundle;
 use clvm_utils::tree_hash;
 use clvmr::allocator::Allocator;
@@ -24,7 +25,29 @@ pub fn get_conditions_from_spendbundle(
     height: u32,
     constants: &ConsensusConstants,
 ) -> Result<SpendBundleConditions, ValidationErr> {
-    let flags = get_flags_for_height_and_constants(height, constants) | MEMPOOL_MODE;
+    Ok(run_spendbundle(
+        a,
+        spend_bundle,
+        max_cost,
+        height,
+        DONT_VALIDATE_SIGNATURE,
+        constants,
+    )?
+    .0)
+}
+
+// returns the conditions for the spendbundle, along with the (public key,
+// message) pairs emitted by the spends (for validating the aggregate signature)
+#[allow(clippy::type_complexity)]
+pub fn run_spendbundle(
+    a: &mut Allocator,
+    spend_bundle: &SpendBundle,
+    max_cost: u64,
+    height: u32,
+    flags: u32,
+    constants: &ConsensusConstants,
+) -> Result<(SpendBundleConditions, Vec<(PublicKey, Vec<u8>)>), ValidationErr> {
+    let flags = get_flags_for_height_and_constants(height, constants) | flags | MEMPOOL_MODE;
 
     // below is an adapted version of the code from run_block_generators::run_block_generator2()
     // it assumes no block references are passed in
@@ -67,9 +90,10 @@ pub fn get_conditions_from_spendbundle(
     }
 
     validate_conditions(a, &ret, &state, a.nil(), flags)?;
+
     assert!(max_cost >= cost_left);
     ret.cost = max_cost - cost_left;
-    Ok(ret)
+    Ok((ret, state.pkm_pairs))
 }
 
 #[cfg(test)]
@@ -133,7 +157,9 @@ mod tests {
             program.as_slice(),
             blocks,
             11_000_000_000,
-            MEMPOOL_MODE,
+            MEMPOOL_MODE | DONT_VALIDATE_SIGNATURE,
+            &Signature::default(),
+            None,
             &TEST_CONSTANTS,
         )
         .expect("run_block_generator2 failed");
@@ -312,7 +338,9 @@ mod tests {
                 &generator_buffer,
                 &block_refs,
                 11_000_000_000,
-                DEFAULT_FLAGS,
+                DEFAULT_FLAGS | DONT_VALIDATE_SIGNATURE,
+                &Signature::default(),
+                None,
                 &TEST_CONSTANTS,
             );
             match block_conds {
diff --git a/crates/chia-consensus/src/spendbundle_validation.rs b/crates/chia-consensus/src/spendbundle_validation.rs
index aef026663..0a790d47c 100644
--- a/crates/chia-consensus/src/spendbundle_validation.rs
+++ b/crates/chia-consensus/src/spendbundle_validation.rs
@@ -1,14 +1,9 @@
 use crate::allocator::make_allocator;
 use crate::consensus_constants::ConsensusConstants;
 use crate::gen::flags::ALLOW_BACKREFS;
-use crate::gen::make_aggsig_final_message::make_aggsig_final_message;
-use crate::gen::opcodes::{
-    AGG_SIG_AMOUNT, AGG_SIG_ME, AGG_SIG_PARENT, AGG_SIG_PARENT_AMOUNT, AGG_SIG_PARENT_PUZZLE,
-    AGG_SIG_PUZZLE, AGG_SIG_PUZZLE_AMOUNT,
-};
 use crate::gen::owned_conditions::OwnedSpendBundleConditions;
 use crate::gen::validation_error::ErrorCode;
-use crate::spendbundle_conditions::get_conditions_from_spendbundle;
+use crate::spendbundle_conditions::run_spendbundle;
 use chia_bls::GTElement;
 use chia_bls::{aggregate_verify_gt, hash_to_g2};
 use chia_protocol::SpendBundle;
@@ -30,8 +25,8 @@ pub fn validate_clvm_and_signature(
 ) -> Result<(OwnedSpendBundleConditions, Vec<ValidationPair>, Duration), ErrorCode> {
     let start_time = Instant::now();
     let mut a = make_allocator(LIMIT_HEAP);
-    let sbc = get_conditions_from_spendbundle(&mut a, spend_bundle, max_cost, height, constants)
-        .map_err(|e| e.1)?;
+    let (sbc, pkm_pairs) =
+        run_spendbundle(&mut a, spend_bundle, max_cost, height, 0, constants).map_err(|e| e.1)?;
     let conditions = OwnedSpendBundleConditions::from(&a, sbc);
 
     // Collect all pairs in a single vector to avoid multiple iterations
@@ -39,45 +34,17 @@ pub fn validate_clvm_and_signature(
 
     let mut aug_msg = Vec::<u8>::new();
 
-    for spend in &conditions.spends {
-        let condition_items_pairs = [
-            (AGG_SIG_PARENT, &spend.agg_sig_parent),
-            (AGG_SIG_PUZZLE, &spend.agg_sig_puzzle),
-            (AGG_SIG_AMOUNT, &spend.agg_sig_amount),
-            (AGG_SIG_PUZZLE_AMOUNT, &spend.agg_sig_puzzle_amount),
-            (AGG_SIG_PARENT_AMOUNT, &spend.agg_sig_parent_amount),
-            (AGG_SIG_PARENT_PUZZLE, &spend.agg_sig_parent_puzzle),
-            (AGG_SIG_ME, &spend.agg_sig_me),
-        ];
-
-        for (condition, items) in condition_items_pairs {
-            for (pk, msg) in items {
-                aug_msg.clear();
-                aug_msg.extend_from_slice(&pk.to_bytes());
-                aug_msg.extend_from_slice(msg.as_slice());
-                make_aggsig_final_message(condition, &mut aug_msg, spend, constants);
-                let aug_hash = hash_to_g2(&aug_msg);
-                let pairing = aug_hash.pair(pk);
-                let mut hasher = Sha256::new();
-                hasher.update(&aug_msg);
-                let aug_msg_hash = hasher.finalize();
-                pairs.push((aug_msg_hash, pairing));
-            }
-        }
-    }
-
-    // Adding unsafe items
-    for (pk, msg) in &conditions.agg_sig_unsafe {
-        let mut aug_msg = pk.to_bytes().to_vec();
-        aug_msg.extend_from_slice(msg.as_ref());
+    for (pk, msg) in pkm_pairs {
+        aug_msg.clear();
+        aug_msg.extend_from_slice(&pk.to_bytes());
+        aug_msg.extend(&msg);
         let aug_hash = hash_to_g2(&aug_msg);
-        let pairing = aug_hash.pair(pk);
-        let mut hasher = Sha256::new();
-        hasher.update(&aug_msg);
-        let aug_msg_hash = hasher.finalize();
-        pairs.push((aug_msg_hash, pairing));
-    }
+        let pairing = aug_hash.pair(&pk);
 
+        let mut key = Sha256::new();
+        key.update(&aug_msg);
+        pairs.push((key.finalize(), pairing));
+    }
     // Verify aggregated signature
     let result = aggregate_verify_gt(
         &spend_bundle.aggregated_signature,
diff --git a/crates/chia-tools/src/bin/analyze-chain.rs b/crates/chia-tools/src/bin/analyze-chain.rs
index 0ab55631d..9d9c58638 100644
--- a/crates/chia-tools/src/bin/analyze-chain.rs
+++ b/crates/chia-tools/src/bin/analyze-chain.rs
@@ -81,6 +81,8 @@ fn main() {
                 &block_refs,
                 ti.cost,
                 flags,
+                &ti.aggregated_signature,
+                None,
                 &TEST_CONSTANTS,
             )
             .expect("failed to run block generator");
diff --git a/crates/chia-tools/src/bin/test-block-generators.rs b/crates/chia-tools/src/bin/test-block-generators.rs
index 0f701d12b..869faaca5 100644
--- a/crates/chia-tools/src/bin/test-block-generators.rs
+++ b/crates/chia-tools/src/bin/test-block-generators.rs
@@ -188,9 +188,17 @@ fn main() {
                     } else {
                         0
                     };
-                let mut conditions =
-                    block_runner(&mut a, generator, &block_refs, ti.cost, flags, constants)
-                        .expect("failed to run block generator");
+                let mut conditions = block_runner(
+                    &mut a,
+                    generator,
+                    &block_refs,
+                    ti.cost,
+                    flags,
+                    &ti.aggregated_signature,
+                    None,
+                    constants,
+                )
+                .expect("failed to run block generator");
 
                 if args.original_generator && height < args.hard_fork_height {
                     // when running pre-hardfork blocks with the post-hard fork
@@ -214,6 +222,8 @@ fn main() {
                         &block_refs,
                         ti.cost,
                         flags,
+                        &ti.aggregated_signature,
+                        None,
                         constants,
                     )
                     .expect("run_block_generator()");
diff --git a/tests/run_gen.py b/tests/run_gen.py
index bd1572b0b..5a28ce643 100755
--- a/tests/run_gen.py
+++ b/tests/run_gen.py
@@ -5,6 +5,8 @@
     SpendBundleConditions,
     run_block_generator2,
     ConsensusConstants,
+    DONT_VALIDATE_SIGNATURE,
+    G2Element,
 )
 from chia_rs.sized_bytes import bytes32
 from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128
@@ -107,7 +109,15 @@ def run_gen(
 
     start_time = perf_counter()
     try:
-        ret = block_runner(generator, block_refs, max_cost, flags, DEFAULT_CONSTANTS)
+        ret = block_runner(
+            generator,
+            block_refs,
+            max_cost,
+            flags | DONT_VALIDATE_SIGNATURE,
+            G2Element(),
+            None,
+            DEFAULT_CONSTANTS,
+        )
         run_time = perf_counter() - start_time
         return ret + (run_time,)
     except Exception as e:
diff --git a/tests/test_get_puzzle_and_solution.py b/tests/test_get_puzzle_and_solution.py
index 2e3ffbc82..0cfcea264 100644
--- a/tests/test_get_puzzle_and_solution.py
+++ b/tests/test_get_puzzle_and_solution.py
@@ -6,6 +6,8 @@
     run_chia_program,
     Program,
     Coin,
+    G2Element,
+    DONT_VALIDATE_SIGNATURE,
 )
 from run_gen import DEFAULT_CONSTANTS
 from chia_rs.sized_bytes import bytes32
@@ -43,7 +45,13 @@ def test_get_puzzle_and_solution_for_coin(input_file: str) -> None:
 
     # first, run the block generator just to list all the spends
     err, conds = run_block_generator2(
-        block, [], MAX_COST, ALLOW_BACKREFS, DEFAULT_CONSTANTS
+        block,
+        [],
+        MAX_COST,
+        ALLOW_BACKREFS | DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     assert err is None
     assert conds is not None
diff --git a/tests/test_run_block_generator.py b/tests/test_run_block_generator.py
index d6f842d51..ec13346a8 100644
--- a/tests/test_run_block_generator.py
+++ b/tests/test_run_block_generator.py
@@ -1,4 +1,9 @@
-from chia_rs import run_block_generator, run_block_generator2
+from chia_rs import (
+    run_block_generator,
+    run_block_generator2,
+    G2Element,
+    DONT_VALIDATE_SIGNATURE,
+)
 from run_gen import print_spend_bundle_conditions, DEFAULT_CONSTANTS
 
 
@@ -14,13 +19,25 @@ def test_run_block_generator_cost() -> None:
         open("generator-tests/block-834768.txt", "r").read().split("\n")[0]
     )
     err, conds = run_block_generator(
-        generator, [], original_consensus_cost, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        original_consensus_cost,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     assert err is None
     assert conds is not None
 
     err2, conds2 = run_block_generator2(
-        generator, [], hard_fork_consensus_cost, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        hard_fork_consensus_cost,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     assert err2 is None
     assert conds2 is not None
@@ -35,14 +52,26 @@ def test_run_block_generator_cost() -> None:
 
     # we exceed the cost limit by 1
     err, conds = run_block_generator(
-        generator, [], original_consensus_cost - 1, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        original_consensus_cost - 1,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     # BLOCK_COST_EXCEEDS_MAX = 23
     assert err == 23
     assert conds is None
 
     err, conds = run_block_generator2(
-        generator, [], hard_fork_consensus_cost - 1, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        hard_fork_consensus_cost - 1,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     # BLOCK_COST_EXCEEDS_MAX = 23
     assert err == 23
@@ -50,7 +79,13 @@ def test_run_block_generator_cost() -> None:
 
     # the byte cost alone exceeds the limit by 1
     err, conds = run_block_generator(
-        generator, [], len(generator) * 12000 - 1, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        len(generator) * 12000 - 1,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     # BLOCK_COST_EXCEEDS_MAX = 23
     assert err == 23
@@ -58,7 +93,13 @@ def test_run_block_generator_cost() -> None:
 
     # the byte cost alone exceeds the limit by 1
     err, conds = run_block_generator2(
-        generator, [], len(generator) * 12000 - 1, 0, DEFAULT_CONSTANTS
+        generator,
+        [],
+        len(generator) * 12000 - 1,
+        DONT_VALIDATE_SIGNATURE,
+        G2Element(),
+        None,
+        DEFAULT_CONSTANTS,
     )
     # BLOCK_COST_EXCEEDS_MAX = 23
     assert err == 23
diff --git a/tests/test_streamable.py b/tests/test_streamable.py
index d26755e8b..cc9b30c0b 100644
--- a/tests/test_streamable.py
+++ b/tests/test_streamable.py
@@ -85,10 +85,10 @@ def test_hash_spend() -> None:
 def test_hash_spend_bundle_conditions() -> None:
 
     a1 = SpendBundleConditions(
-        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456
+        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False
     )
     a2 = SpendBundleConditions(
-        [], 1001, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456
+        [], 1001, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False
     )
     b = hash(a1)
     c = hash(a2)
@@ -391,7 +391,7 @@ def test_missing_field() -> None:
 def test_json_spend_bundle_conditions() -> None:
 
     a = SpendBundleConditions(
-        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456
+        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False
     )
 
     assert a.to_json_dict() == {
@@ -405,13 +405,14 @@ def test_json_spend_bundle_conditions() -> None:
         "cost": 12345678,
         "removal_amount": 123,
         "addition_amount": 456,
+        "validated_signature": False,
     }
 
 
 def test_from_json_spend_bundle_conditions() -> None:
 
     a = SpendBundleConditions(
-        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456
+        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False
     )
     b = SpendBundleConditions.from_json_dict(
         {
@@ -425,6 +426,7 @@ def test_from_json_spend_bundle_conditions() -> None:
             "cost": 12345678,
             "removal_amount": 123,
             "addition_amount": 456,
+            "validated_signature": False,
         }
     )
     assert a == b
@@ -466,7 +468,7 @@ def test_copy_spend() -> None:
 def test_copy_spend_bundle_conditions() -> None:
 
     a = SpendBundleConditions(
-        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456
+        [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False
     )
     b = copy.copy(a)
 
diff --git a/wheel/generate_type_stubs.py b/wheel/generate_type_stubs.py
index a0b81eab0..dbc988a97 100644
--- a/wheel/generate_type_stubs.py
+++ b/wheel/generate_type_stubs.py
@@ -281,11 +281,11 @@ def supports_fast_forward(spend: CoinSpend) -> bool : ...
 def fast_forward_singleton(spend: CoinSpend, new_coin: Coin, new_parent: Coin) -> bytes: ...
 
 def run_block_generator(
-    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, constants: ConsensusConstants
+    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, signature: G2Element, bls_cache: Optional[BLSCache], constants: ConsensusConstants
 ) -> Tuple[Optional[int], Optional[SpendBundleConditions]]: ...
 
 def run_block_generator2(
-    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, constants: ConsensusConstants
+    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, signature: G2Element, bls_cache: Optional[BLSCache], constants: ConsensusConstants
 ) -> Tuple[Optional[int], Optional[SpendBundleConditions]]: ...
 
 def confirm_included_already_hashed(
@@ -325,6 +325,7 @@ def get_flags_for_height_and_constants(
 LIMIT_HEAP: int = ...
 MEMPOOL_MODE: int = ...
 ALLOW_BACKREFS: int = ...
+DONT_VALIDATE_SIGNATURE: int = ...
 
 ELIGIBLE_FOR_DEDUP: int = ...
 ELIGIBLE_FOR_FF: int = ...
@@ -486,6 +487,7 @@ def __init__(
             "cost: int",
             "removal_amount: int",
             "addition_amount: int",
+            "validated_signature: bool",
         ],
     )
 
diff --git a/wheel/python/chia_rs/chia_rs.pyi b/wheel/python/chia_rs/chia_rs.pyi
index 05147d0de..b30b26af8 100644
--- a/wheel/python/chia_rs/chia_rs.pyi
+++ b/wheel/python/chia_rs/chia_rs.pyi
@@ -23,11 +23,11 @@ def supports_fast_forward(spend: CoinSpend) -> bool : ...
 def fast_forward_singleton(spend: CoinSpend, new_coin: Coin, new_parent: Coin) -> bytes: ...
 
 def run_block_generator(
-    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, constants: ConsensusConstants
+    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, signature: G2Element, bls_cache: Optional[BLSCache], constants: ConsensusConstants
 ) -> Tuple[Optional[int], Optional[SpendBundleConditions]]: ...
 
 def run_block_generator2(
-    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, constants: ConsensusConstants
+    program: ReadableBuffer, block_refs: List[ReadableBuffer], max_cost: int, flags: int, signature: G2Element, bls_cache: Optional[BLSCache], constants: ConsensusConstants
 ) -> Tuple[Optional[int], Optional[SpendBundleConditions]]: ...
 
 def confirm_included_already_hashed(
@@ -67,6 +67,7 @@ STRICT_ARGS_COUNT: int = ...
 LIMIT_HEAP: int = ...
 MEMPOOL_MODE: int = ...
 ALLOW_BACKREFS: int = ...
+DONT_VALIDATE_SIGNATURE: int = ...
 
 ELIGIBLE_FOR_DEDUP: int = ...
 ELIGIBLE_FOR_FF: int = ...
@@ -341,6 +342,7 @@ class SpendBundleConditions:
     cost: int
     removal_amount: int
     addition_amount: int
+    validated_signature: bool
     def __init__(
         self,
         spends: Sequence[SpendConditions],
@@ -352,7 +354,8 @@ class SpendBundleConditions:
         agg_sig_unsafe: Sequence[Tuple[G1Element, bytes]],
         cost: int,
         removal_amount: int,
-        addition_amount: int
+        addition_amount: int,
+        validated_signature: bool
     ) -> None: ...
     def __hash__(self) -> int: ...
     def __repr__(self) -> str: ...
@@ -380,7 +383,8 @@ class SpendBundleConditions:
         agg_sig_unsafe: Union[ List[Tuple[G1Element, bytes]], _Unspec] = _Unspec(),
         cost: Union[ int, _Unspec] = _Unspec(),
         removal_amount: Union[ int, _Unspec] = _Unspec(),
-        addition_amount: Union[ int, _Unspec] = _Unspec()) -> SpendBundleConditions: ...
+        addition_amount: Union[ int, _Unspec] = _Unspec(),
+        validated_signature: Union[ bool, _Unspec] = _Unspec()) -> SpendBundleConditions: ...
 
 @final
 class BlockRecord:
diff --git a/wheel/src/api.rs b/wheel/src/api.rs
index f100bd97c..58b70be98 100644
--- a/wheel/src/api.rs
+++ b/wheel/src/api.rs
@@ -2,7 +2,7 @@ use crate::run_generator::{py_to_slice, run_block_generator, run_block_generator
 use chia_consensus::allocator::make_allocator;
 use chia_consensus::consensus_constants::ConsensusConstants;
 use chia_consensus::gen::flags::{
-    ALLOW_BACKREFS, MEMPOOL_MODE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT,
+    ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT,
 };
 use chia_consensus::gen::owned_conditions::{OwnedSpendBundleConditions, OwnedSpendConditions};
 use chia_consensus::gen::run_block_generator::setup_generator_args;
@@ -484,6 +484,7 @@ pub fn chia_rs(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
     m.add("STRICT_ARGS_COUNT", STRICT_ARGS_COUNT)?;
     m.add("MEMPOOL_MODE", MEMPOOL_MODE)?;
     m.add("ALLOW_BACKREFS", ALLOW_BACKREFS)?;
+    m.add("DONT_VALIDATE_SIGNATURE", DONT_VALIDATE_SIGNATURE)?;
 
     // Chia classes
     m.add_class::<Coin>()?;
diff --git a/wheel/src/run_generator.rs b/wheel/src/run_generator.rs
index 8c628db75..eb92d0a14 100644
--- a/wheel/src/run_generator.rs
+++ b/wheel/src/run_generator.rs
@@ -1,3 +1,4 @@
+use chia_bls::{BlsCache, Signature};
 use chia_consensus::allocator::make_allocator;
 use chia_consensus::consensus_constants::ConsensusConstants;
 use chia_consensus::gen::owned_conditions::OwnedSpendBundleConditions;
@@ -17,12 +18,16 @@ pub fn py_to_slice<'a>(buf: PyBuffer<u8>) -> &'a [u8] {
 }
 
 #[pyfunction]
+#[pyo3(signature = (program, block_refs, max_cost, flags, signature, bls_cache, constants))]
+#[allow(clippy::too_many_arguments)]
 pub fn run_block_generator<'a>(
     py: Python<'a>,
     program: PyBuffer<u8>,
     block_refs: &Bound<'_, PyList>,
     max_cost: Cost,
     flags: u32,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
     constants: &ConsensusConstants,
 ) -> (Option<u32>, Option<OwnedSpendBundleConditions>) {
     let mut allocator = make_allocator(flags);
@@ -39,8 +44,16 @@ pub fn run_block_generator<'a>(
     let program = py_to_slice::<'a>(program);
 
     py.allow_threads(|| {
-        match native_run_block_generator(&mut allocator, program, refs, max_cost, flags, constants)
-        {
+        match native_run_block_generator(
+            &mut allocator,
+            program,
+            refs,
+            max_cost,
+            flags,
+            signature,
+            bls_cache,
+            constants,
+        ) {
             Ok(spend_bundle_conds) => (
                 None,
                 Some(OwnedSpendBundleConditions::from(
@@ -57,12 +70,16 @@ pub fn run_block_generator<'a>(
 }
 
 #[pyfunction]
+#[pyo3(signature = (program, block_refs, max_cost, flags, signature, bls_cache, constants))]
+#[allow(clippy::too_many_arguments)]
 pub fn run_block_generator2<'a>(
     py: Python<'a>,
     program: PyBuffer<u8>,
     block_refs: &Bound<'_, PyList>,
     max_cost: Cost,
     flags: u32,
+    signature: &Signature,
+    bls_cache: Option<&mut BlsCache>,
     constants: &ConsensusConstants,
 ) -> (Option<u32>, Option<OwnedSpendBundleConditions>) {
     let mut allocator = make_allocator(flags);
@@ -80,8 +97,16 @@ pub fn run_block_generator2<'a>(
     let program = py_to_slice::<'a>(program);
 
     py.allow_threads(|| {
-        match native_run_block_generator2(&mut allocator, program, refs, max_cost, flags, constants)
-        {
+        match native_run_block_generator2(
+            &mut allocator,
+            program,
+            refs,
+            max_cost,
+            flags,
+            signature,
+            bls_cache,
+            constants,
+        ) {
             Ok(spend_bundle_conds) => (
                 None,
                 Some(OwnedSpendBundleConditions::from(

From 5948190008168c57da5050fe936fa691cafb83ea Mon Sep 17 00:00:00 2001
From: arvidn <arvid@libtorrent.org>
Date: Tue, 8 Oct 2024 21:12:11 +0200
Subject: [PATCH 2/6] fix comment

---
 crates/chia-consensus/src/gen/owned_conditions.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/chia-consensus/src/gen/owned_conditions.rs b/crates/chia-consensus/src/gen/owned_conditions.rs
index 3ce68a3d0..f39b8f25e 100644
--- a/crates/chia-consensus/src/gen/owned_conditions.rs
+++ b/crates/chia-consensus/src/gen/owned_conditions.rs
@@ -67,7 +67,7 @@ pub struct OwnedSpendBundleConditions {
     pub removal_amount: u128,
     // the sum of all amounts of CREATE_COIN conditions
     pub addition_amount: u128,
-    // set if the aggregate signature of the block/spend bundle were
+    // set if the aggregate signature of the block/spend bundle was
     // successfully validated
     pub validated_signature: bool,
 }

From 759c6f02d2f5d0b5cb402908f8d62cd8a073e985 Mon Sep 17 00:00:00 2001
From: arvidn <arvid@libtorrent.org>
Date: Tue, 8 Oct 2024 21:17:02 +0200
Subject: [PATCH 3/6] fix BlsCache update

---
 crates/chia-bls/src/bls_cache.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/chia-bls/src/bls_cache.rs b/crates/chia-bls/src/bls_cache.rs
index 343d73cf1..e3b8cdcf9 100644
--- a/crates/chia-bls/src/bls_cache.rs
+++ b/crates/chia-bls/src/bls_cache.rs
@@ -86,7 +86,7 @@ impl BlsCache {
         let mut hasher = Sha256::new();
         hasher.update(aug_msg.as_ref());
         let hash: [u8; 32] = hasher.finalize();
-        self.cache.put(hash, gt);
+        self.cache.lock().expect("cache").put(hash, gt);
     }
 }
 

From 9249773072417edf619c52a31d8afeeb7c1af4b6 Mon Sep 17 00:00:00 2001
From: Arvid Norberg <arvid@libtorrent.org>
Date: Wed, 9 Oct 2024 10:06:49 +0200
Subject: [PATCH 4/6] add option to not validate signatures, in
 test-block-generators

---
 crates/chia-tools/src/bin/test-block-generators.rs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/crates/chia-tools/src/bin/test-block-generators.rs b/crates/chia-tools/src/bin/test-block-generators.rs
index 869faaca5..5f2c1f791 100644
--- a/crates/chia-tools/src/bin/test-block-generators.rs
+++ b/crates/chia-tools/src/bin/test-block-generators.rs
@@ -3,7 +3,7 @@ use clap::Parser;
 use chia_bls::PublicKey;
 use chia_consensus::consensus_constants::TEST_CONSTANTS;
 use chia_consensus::gen::conditions::{NewCoin, SpendBundleConditions, SpendConditions};
-use chia_consensus::gen::flags::{ALLOW_BACKREFS, MEMPOOL_MODE};
+use chia_consensus::gen::flags::{ALLOW_BACKREFS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE};
 use chia_consensus::gen::run_block_generator::{run_block_generator, run_block_generator2};
 use chia_tools::iterate_tx_blocks;
 use clvmr::allocator::NodePtr;
@@ -30,6 +30,10 @@ struct Args {
     #[arg(long, default_value_t = false)]
     mempool: bool,
 
+    /// Don't validate block signatures (saves time)
+    #[arg(long, default_value_t = false)]
+    skip_signature_validation: bool,
+
     /// Compare the output from the default ROM running in consensus mode
     /// against the hard-fork rules for executing block generators. After the
     /// hard fork, the CLVM ROM implementation is no longer expected to work, so
@@ -187,6 +191,11 @@ fn main() {
                         ALLOW_BACKREFS
                     } else {
                         0
+                    }
+                    | if args.skip_signature_validation {
+                        DONT_VALIDATE_SIGNATURE
+                    } else {
+                        0
                     };
                 let mut conditions = block_runner(
                     &mut a,

From 082f6efd7182e2cdb4f6fb98a03b136405584126 Mon Sep 17 00:00:00 2001
From: Arvid Norberg <arvid@libtorrent.org>
Date: Wed, 9 Oct 2024 11:25:36 +0200
Subject: [PATCH 5/6] restore checking invalid message suffixes for agg sig
 unsafe, even when DONT_VALIDATE_SIGNATURE is set. For backwards compatibility

---
 crates/chia-consensus/src/gen/conditions.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs
index a15b23169..ed6837d4c 100644
--- a/crates/chia-consensus/src/gen/conditions.rs
+++ b/crates/chia-consensus/src/gen/conditions.rs
@@ -1197,11 +1197,11 @@ pub fn parse_conditions<V: SpendVisitor>(
                 }
             }
             Condition::AggSigUnsafe(pk, msg) => {
+                // AGG_SIG_UNSAFE messages are not allowed to end with the
+                // suffix added to other AGG_SIG_* conditions
+                check_agg_sig_unsafe_message(a, msg, constants)?;
                 ret.agg_sig_unsafe.push((to_key(a, pk)?, msg));
                 if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
-                    // AGG_SIG_UNSAFE messages are not allowed to end with the
-                    // suffix added to other AGG_SIG_* conditions
-                    check_agg_sig_unsafe_message(a, msg, constants)?;
                     state
                         .pkm_pairs
                         .push((to_key(a, pk)?, a.atom(msg).as_ref().to_vec()));

From e6414dd4b75feed738cf759181e80da41bfaaada Mon Sep 17 00:00:00 2001
From: arvidn <arvid@libtorrent.org>
Date: Wed, 9 Oct 2024 19:01:11 +0200
Subject: [PATCH 6/6] address review comments

---
 crates/chia-consensus/src/gen/conditions.rs   | 32 ++++++++-----------
 .../src/spendbundle_conditions.rs             |  4 +--
 .../src/spendbundle_validation.rs             |  2 +-
 3 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs
index ed6837d4c..435ecf843 100644
--- a/crates/chia-consensus/src/gen/conditions.rs
+++ b/crates/chia-consensus/src/gen/conditions.rs
@@ -23,7 +23,7 @@ use crate::gen::messages::{Message, SpendId};
 use crate::gen::spend_visitor::SpendVisitor;
 use crate::gen::validation_error::check_nil;
 use chia_bls::{aggregate_verify, BlsCache, PublicKey, Signature};
-use chia_protocol::Bytes32;
+use chia_protocol::{Bytes, Bytes32};
 use chia_sha2::Sha256;
 use clvmr::allocator::{Allocator, NodePtr, SExp};
 use clvmr::cost::Cost;
@@ -785,7 +785,7 @@ pub struct ParseState {
     // DONT_VALIDATE_SIGNATURE flag is set
     // TODO: We would probably save heap allocations by turning this into a
     // blst_pairing object.
-    pub pkm_pairs: Vec<(PublicKey, Vec<u8>)>,
+    pub pkm_pairs: Vec<(PublicKey, Bytes)>,
 }
 
 // returns (parent-id, puzzle-hash, amount, condition-list)
@@ -1136,7 +1136,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     let mut msg = a.atom(msg).as_ref().to_vec();
                     msg.extend((*spend.coin_id).as_slice());
                     msg.extend(constants.agg_sig_me_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigParent(pk, msg) => {
@@ -1145,7 +1145,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     let mut msg = a.atom(msg).as_ref().to_vec();
                     msg.extend(a.atom(spend.parent_id).as_ref());
                     msg.extend(constants.agg_sig_parent_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigPuzzle(pk, msg) => {
@@ -1154,7 +1154,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     let mut msg = a.atom(msg).as_ref().to_vec();
                     msg.extend(a.atom(spend.puzzle_hash).as_ref());
                     msg.extend(constants.agg_sig_puzzle_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigAmount(pk, msg) => {
@@ -1163,7 +1163,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     let mut msg = a.atom(msg).as_ref().to_vec();
                     msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
                     msg.extend(constants.agg_sig_amount_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigPuzzleAmount(pk, msg) => {
@@ -1173,7 +1173,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     msg.extend(a.atom(spend.puzzle_hash).as_ref());
                     msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
                     msg.extend(constants.agg_sig_puzzle_amount_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigParentAmount(pk, msg) => {
@@ -1183,7 +1183,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     msg.extend(a.atom(spend.parent_id).as_ref());
                     msg.extend(u64_to_bytes(spend.coin_amount).as_slice());
                     msg.extend(constants.agg_sig_parent_amount_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigParentPuzzle(pk, msg) => {
@@ -1193,7 +1193,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                     msg.extend(a.atom(spend.parent_id).as_ref());
                     msg.extend(a.atom(spend.puzzle_hash).as_ref());
                     msg.extend(constants.agg_sig_parent_puzzle_additional_data.as_slice());
-                    state.pkm_pairs.push((to_key(a, pk)?, msg));
+                    state.pkm_pairs.push((to_key(a, pk)?, msg.into()));
                 }
             }
             Condition::AggSigUnsafe(pk, msg) => {
@@ -1204,7 +1204,7 @@ pub fn parse_conditions<V: SpendVisitor>(
                 if (flags & DONT_VALIDATE_SIGNATURE) == 0 {
                     state
                         .pkm_pairs
-                        .push((to_key(a, pk)?, a.atom(msg).as_ref().to_vec()));
+                        .push((to_key(a, pk)?, a.atom(msg).as_ref().to_vec().into()));
                 }
             }
             Condition::Softfork(cost) => {
@@ -4760,14 +4760,10 @@ fn test_agg_sig(
             b"foobar",
         ));
     }
-    match cond_test_sig(puzzle.as_str(), &signature, cache, 0) {
-        Ok(..) => {
-            assert!(expect_pass);
-        }
-        Err(..) => {
-            assert!(!expect_pass);
-        }
-    }
+    assert_eq!(
+        expect_pass,
+        cond_test_sig(puzzle.as_str(), &signature, cache, 0).is_ok()
+    );
 }
 
 // the message condition takes a mode-parameter. This is a 6-bit integer that
diff --git a/crates/chia-consensus/src/spendbundle_conditions.rs b/crates/chia-consensus/src/spendbundle_conditions.rs
index 22f99e53f..236cf33a3 100644
--- a/crates/chia-consensus/src/spendbundle_conditions.rs
+++ b/crates/chia-consensus/src/spendbundle_conditions.rs
@@ -8,7 +8,7 @@ use crate::gen::solution_generator::calculate_generator_length;
 use crate::gen::validation_error::ValidationErr;
 use crate::spendbundle_validation::get_flags_for_height_and_constants;
 use chia_bls::PublicKey;
-use chia_protocol::SpendBundle;
+use chia_protocol::{Bytes, SpendBundle};
 use clvm_utils::tree_hash;
 use clvmr::allocator::Allocator;
 use clvmr::chia_dialect::ChiaDialect;
@@ -46,7 +46,7 @@ pub fn run_spendbundle(
     height: u32,
     flags: u32,
     constants: &ConsensusConstants,
-) -> Result<(SpendBundleConditions, Vec<(PublicKey, Vec<u8>)>), ValidationErr> {
+) -> Result<(SpendBundleConditions, Vec<(PublicKey, Bytes)>), ValidationErr> {
     let flags = get_flags_for_height_and_constants(height, constants) | flags | MEMPOOL_MODE;
 
     // below is an adapted version of the code from run_block_generators::run_block_generator2()
diff --git a/crates/chia-consensus/src/spendbundle_validation.rs b/crates/chia-consensus/src/spendbundle_validation.rs
index 0a790d47c..814d5dcca 100644
--- a/crates/chia-consensus/src/spendbundle_validation.rs
+++ b/crates/chia-consensus/src/spendbundle_validation.rs
@@ -37,7 +37,7 @@ pub fn validate_clvm_and_signature(
     for (pk, msg) in pkm_pairs {
         aug_msg.clear();
         aug_msg.extend_from_slice(&pk.to_bytes());
-        aug_msg.extend(&msg);
+        aug_msg.extend(&*msg);
         let aug_hash = hash_to_g2(&aug_msg);
         let pairing = aug_hash.pair(&pk);