Skip to content
3 changes: 3 additions & 0 deletions crates/chia-consensus/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ pub const COST_CONDITIONS: u32 = 0x80_0000;

/// A combination of flags suitable for mempool-mode, with stricter checking
pub const MEMPOOL_MODE: u32 = CLVM_MEMPOOL_MODE | NO_UNKNOWN_CONDS | STRICT_ARGS_COUNT;

// This flag sets the generator to use the simpler generator rules
pub const SIMPLE_GENERATOR: u32 = 0x1600_0000;
44 changes: 43 additions & 1 deletion crates/chia-consensus/src/run_block_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use crate::conditions::{
ParseState, SpendBundleConditions,
};
use crate::consensus_constants::ConsensusConstants;
use crate::flags::DONT_VALIDATE_SIGNATURE;
use crate::flags::{DONT_VALIDATE_SIGNATURE, SIMPLE_GENERATOR};
use crate::validation_error::{first, ErrorCode, ValidationErr};
use chia_bls::{BlsCache, Signature};
use chia_protocol::{BytesImpl, Coin, CoinSpend, Program};
use chia_puzzles::{CHIALISP_DESERIALISATION, ROM_BOOTSTRAP_GENERATOR};
use clvm_traits::FromClvm;
use clvm_traits::MatchByte;
use clvm_utils::{tree_hash_cached, TreeCache};
use clvmr::allocator::{Allocator, NodePtr};
use clvmr::chia_dialect::ChiaDialect;
Expand Down Expand Up @@ -86,13 +87,15 @@ pub fn run_block_generator<GenBuf: AsRef<[u8]>, I: IntoIterator<Item = GenBuf>>(
where
<I as IntoIterator>::IntoIter: DoubleEndedIterator,
{
check_generator_quote(a, program, flags)?;
let mut cost_left = max_cost;
let byte_cost = program.len() as u64 * constants.cost_per_byte;

subtract_cost(a, &mut cost_left, byte_cost)?;

let rom_generator = node_from_bytes(a, &ROM_BOOTSTRAP_GENERATOR)?;
let program = node_from_bytes_backrefs(a, program)?;
check_generator_node(a, program, flags)?;

// this is setting up the arguments to be passed to the generator ROM,
// not the actual generator (the ROM does that).
Expand Down Expand Up @@ -154,6 +157,39 @@ fn extract_n<const N: usize>(
Ok(ret)
}

// this function checks if the generator start with a quote
// this is required after the SIMPLE_GENERATOR fork is active
#[inline]
pub fn check_generator_quote(
a: &Allocator,
program: &[u8],
flags: u32,
) -> Result<(), ValidationErr> {
if flags & SIMPLE_GENERATOR == 0 || program.starts_with(&[0xff, 0x01]) {
Ok(())
} else {
Err(ValidationErr(a.nil(), ErrorCode::ComplexGeneratorReceived))
}
}

// this function is mostly the same as above but is a double check in case of
// discrepancies in serialized vs deserialized forms
#[inline]
pub fn check_generator_node(
a: &Allocator,
program: NodePtr,
flags: u32,
) -> Result<(), ValidationErr> {
if flags & SIMPLE_GENERATOR == 0 {
return Ok(());
}
// this expects an atom with a single byte value of 1 as the first value in the list
match <(MatchByte<1>, NodePtr)>::from_clvm(a, program) {
Err(..) => Err(ValidationErr(a.nil(), ErrorCode::ComplexGeneratorReceived)),
_ => Ok(()),
}
}

/// This has the same behavior as run_block_generator() but implements the
/// generator ROM in rust instead of using the CLVM implementation.
/// it is not backwards compatible in the CLVM cost computation (in this version
Expand All @@ -174,12 +210,14 @@ pub fn run_block_generator2<GenBuf: AsRef<[u8]>, I: IntoIterator<Item = GenBuf>>
where
<I as IntoIterator>::IntoIter: DoubleEndedIterator,
{
check_generator_quote(a, program, flags)?;
let byte_cost = program.len() as u64 * constants.cost_per_byte;

let mut cost_left = max_cost;
subtract_cost(a, &mut cost_left, byte_cost)?;

let program = node_from_bytes_backrefs(a, program)?;
check_generator_node(a, program, flags)?;

let args = setup_generator_args(a, block_refs)?;
let dialect = ChiaDialect::new(flags);
Expand Down Expand Up @@ -264,9 +302,11 @@ where
<I as IntoIterator>::IntoIter: DoubleEndedIterator,
{
let mut a = make_allocator(flags);
check_generator_quote(&a, generator.as_ref(), flags)?;
let mut output = Vec::<CoinSpend>::new();

let program = node_from_bytes_backrefs(&mut a, generator)?;
check_generator_node(&a, program, flags)?;
let args = setup_generator_args(&mut a, refs)?;
let dialect = ChiaDialect::new(flags);

Expand Down Expand Up @@ -337,9 +377,11 @@ where
<I as IntoIterator>::IntoIter: DoubleEndedIterator,
{
let mut a = make_allocator(flags);
check_generator_quote(&a, generator.as_ref(), flags)?;
let mut output = Vec::<(CoinSpend, Vec<(u32, Vec<Vec<u8>>)>)>::new();

let program = node_from_bytes_backrefs(&mut a, generator)?;
check_generator_node(&a, program, flags)?;
let args = setup_generator_args(&mut a, refs)?;
let dialect = ChiaDialect::new(flags);

Expand Down
4 changes: 2 additions & 2 deletions crates/chia-consensus/src/spendbundle_validation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::allocator::make_allocator;
use crate::consensus_constants::ConsensusConstants;
use crate::flags::COST_CONDITIONS;
use crate::flags::{COST_CONDITIONS, SIMPLE_GENERATOR};
use crate::owned_conditions::OwnedSpendBundleConditions;
use crate::spendbundle_conditions::run_spendbundle;
use crate::validation_error::{ErrorCode, ValidationErr};
Expand Down Expand Up @@ -85,7 +85,7 @@ pub fn get_flags_for_height_and_constants(height: u32, constants: &ConsensusCons
// In hard fork 2, we enable the keccak operator outside the softfork guard
let mut flags: u32 = 0;
if height >= constants.hard_fork2_height {
flags |= ENABLE_KECCAK_OPS_OUTSIDE_GUARD | COST_CONDITIONS;
flags |= ENABLE_KECCAK_OPS_OUTSIDE_GUARD | COST_CONDITIONS | SIMPLE_GENERATOR;
}
flags
}
Expand Down
55 changes: 54 additions & 1 deletion crates/chia-consensus/src/test_generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ use super::run_block_generator::{
};
use crate::allocator::make_allocator;
use crate::consensus_constants::TEST_CONSTANTS;
use crate::flags::{COST_CONDITIONS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE};
use crate::flags::{COST_CONDITIONS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE, SIMPLE_GENERATOR};
use crate::run_block_generator::check_generator_node;
use crate::validation_error::ErrorCode;
use chia_bls::Signature;
use chia_protocol::Program;
use chia_protocol::{Bytes, Bytes48};
use clvmr::allocator::NodePtr;
use clvmr::serde::node_from_bytes_backrefs;
use clvmr::Allocator;
use std::iter::zip;
use text_diff::diff;
Expand Down Expand Up @@ -228,6 +231,7 @@ pub(crate) fn print_diff(output: &str, expected: &str) {
#[case("recursion-pairs")]
#[case("unknown-condition")]
#[case("duplicate-messages")]
#[case("non-quote-0001-start")]
fn run_generator(#[case] name: &str) {
use std::fs::{read_to_string, write};

Expand Down Expand Up @@ -278,6 +282,55 @@ fn run_generator(#[case] name: &str) {
flags |= COST_CONDITIONS;
}

if ![
"single-coin-only-garbage",
"many-coins-announcement-cap",
"puzzle-hash-stress-test",
"puzzle-hash-stress-tree",
"aa-million-message-spends",
"aa-million-messages",
"29500-remarks-procedural",
"100000-remarks-prefab",
"3000000-conditions-single-coin",
"block-4671894",
"block-225758",
"infinite-recursion1",
"infinite-recursion2",
"infinite-recursion3",
"infinite-recursion4",
"recursion-pairs",
"non-quote-0001-start", // this will fail the generator format check before it fails at runtime
]
.contains(&name)
{
// test that we allow quoted generators with the flag
flags |= SIMPLE_GENERATOR;
} else {
// lets test that the procedural generators are filtered with the flag
let mut a = make_allocator(flags);
let test_conds = run_block_generator2(
&mut a,
&generator,
&block_refs,
11_000_000_000,
flags | DONT_VALIDATE_SIGNATURE | SIMPLE_GENERATOR,
&Signature::default(),
None,
&TEST_CONSTANTS,
);
assert!(test_conds.is_err());
assert_eq!(
test_conds.unwrap_err().1,
ErrorCode::ComplexGeneratorReceived
);
// now lets specifically check the node generator check
let program =
node_from_bytes_backrefs(&mut a, generator.as_ref()).expect("should be ok");
let res = check_generator_node(&a, program, flags | SIMPLE_GENERATOR);
assert!(res.is_err());
assert_eq!(res.unwrap_err().1, ErrorCode::ComplexGeneratorReceived);
}

println!("flags: {flags:x}");
let mut a2 = make_allocator(flags);
let conds2 = run_block_generator2(
Expand Down
2 changes: 2 additions & 0 deletions crates/chia-consensus/src/validation_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ pub enum ErrorCode {
InvalidMessageMode,
InvalidCoinId,
MessageNotSentOrReceived,
ComplexGeneratorReceived,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
Expand Down Expand Up @@ -358,6 +359,7 @@ impl From<ErrorCode> for u32 {
ErrorCode::InvalidMessageMode => 145,
ErrorCode::InvalidCoinId => 146,
ErrorCode::MessageNotSentOrReceived => 147,
ErrorCode::ComplexGeneratorReceived => 148,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions generator-tests/non-quote-0001-start.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ff820001ffffffa00101010101010101010101010101010101010101010101010101010101010101ffff01ffff33ffa06162616261626162616261626162616261626162616261626162616261626162ff0580ffff33ffa06162616261626162616261626162616261626162616261626162616261626162ff048080ff7bffff80ffff018080808080
FAILED: 117
1 change: 1 addition & 0 deletions wheel/generate_type_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ def expected_plot_size(
DONT_VALIDATE_SIGNATURE: int = ...
COMPUTE_FINGERPRINT: int = ...
COST_CONDITIONS: int = ...
SIMPLE_GENERATOR: int = ...

ELIGIBLE_FOR_DEDUP: int = ...
ELIGIBLE_FOR_FF: int = ...
Expand Down
1 change: 1 addition & 0 deletions wheel/python/chia_rs/chia_rs.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ MEMPOOL_MODE: int = ...
DONT_VALIDATE_SIGNATURE: int = ...
COMPUTE_FINGERPRINT: int = ...
COST_CONDITIONS: int = ...
SIMPLE_GENERATOR: int = ...

ELIGIBLE_FOR_DEDUP: int = ...
ELIGIBLE_FOR_FF: int = ...
Expand Down
3 changes: 2 additions & 1 deletion wheel/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use chia_consensus::check_time_locks::py_check_time_locks;
use chia_consensus::consensus_constants::ConsensusConstants;
use chia_consensus::flags::{
COMPUTE_FINGERPRINT, COST_CONDITIONS, DONT_VALIDATE_SIGNATURE, MEMPOOL_MODE, NO_UNKNOWN_CONDS,
STRICT_ARGS_COUNT,
SIMPLE_GENERATOR, STRICT_ARGS_COUNT,
};
use chia_consensus::merkle_set::compute_merkle_set_root as compute_merkle_root_impl;
use chia_consensus::merkle_tree::{validate_merkle_proof, MerkleSet};
Expand Down Expand Up @@ -795,6 +795,7 @@ pub fn chia_rs(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("DONT_VALIDATE_SIGNATURE", DONT_VALIDATE_SIGNATURE)?;
m.add("COMPUTE_FINGERPRINT", COMPUTE_FINGERPRINT)?;
m.add("COST_CONDITIONS", COST_CONDITIONS)?;
m.add("SIMPLE_GENERATOR", SIMPLE_GENERATOR)?;

m.add_class::<PyPlotParam>()?;

Expand Down
Loading