Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add functions get_flags_for_height_and_constants() and get_conditions_from_spendbundle() #634

Merged
merged 15 commits into from
Aug 6, 2024
Merged
6 changes: 5 additions & 1 deletion crates/chia-consensus/src/gen/run_block_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ use clvmr::run_program::run_program;
use clvmr::serde::{node_from_bytes, node_from_bytes_backrefs, node_from_bytes_backrefs_record};
use std::collections::{HashMap, HashSet};

fn subtract_cost(a: &Allocator, cost_left: &mut Cost, subtract: Cost) -> Result<(), ValidationErr> {
pub fn subtract_cost(
a: &Allocator,
cost_left: &mut Cost,
subtract: Cost,
) -> Result<(), ValidationErr> {
if subtract > *cost_left {
Err(ValidationErr(a.nil(), ErrorCode::CostExceeded))
} else {
Expand Down
2 changes: 2 additions & 0 deletions crates/chia-consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pub mod gen;
pub mod generator_rom;
pub mod merkle_set;
pub mod merkle_tree;
pub mod spendbundle_conditions;
pub mod spendbundle_validation;
107 changes: 107 additions & 0 deletions crates/chia-consensus/src/spendbundle_conditions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use crate::allocator::make_allocator;
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::owned_conditions::OwnedSpendBundleConditions;
use crate::gen::run_block_generator::subtract_cost;
use crate::gen::validation_error::ValidationErr;
use crate::spendbundle_validation::get_flags_for_height_and_constants;
use chia_protocol::SpendBundle;
use clvm_utils::tree_hash;
use clvmr::allocator::Allocator;
use clvmr::chia_dialect::ChiaDialect;
use clvmr::chia_dialect::LIMIT_HEAP;
use clvmr::reduction::Reduction;
use clvmr::run_program::run_program;
use clvmr::serde::node_from_bytes;

pub fn get_conditions_from_spendbundle(
spend_bundle: &SpendBundle,
Rigidity marked this conversation as resolved.
Show resolved Hide resolved
max_cost: u64,
height: u32,
constants: &ConsensusConstants,
) -> Result<OwnedSpendBundleConditions, ValidationErr> {
let flags = get_flags_for_height_and_constants(height, constants) | 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
let mut cost_left = max_cost;
let dialect = ChiaDialect::new(flags);
let mut a: Allocator = make_allocator(LIMIT_HEAP);
let mut ret = SpendBundleConditions::default();
let mut state = ParseState::default();

for coin_spend in &spend_bundle.coin_spends {
// process the spend
let puz = node_from_bytes(&mut a, coin_spend.puzzle_reveal.as_slice())?;
let sol = node_from_bytes(&mut a, coin_spend.solution.as_slice())?;
let parent = a.new_atom(coin_spend.coin.parent_coin_info.as_slice())?;
let amount = a.new_number(coin_spend.coin.amount.into())?;
let Reduction(clvm_cost, conditions) = run_program(&mut a, &dialect, puz, sol, cost_left)?;

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

let buf = tree_hash(&a, puz);
let puzzle_hash = a.new_atom(&buf)?;
process_single_spend::<MempoolVisitor>(
&a,
&mut ret,
&mut state,
parent,
puzzle_hash,
amount,
conditions,
flags,
&mut cost_left,
constants,
)?;
}

validate_conditions(&a, &ret, state, a.nil(), flags)?;
assert!(max_cost >= cost_left);
ret.cost = max_cost - cost_left;
let osbc = OwnedSpendBundleConditions::from(&a, ret);
Ok(osbc)
}

#[cfg(test)]
mod tests {
use crate::consensus_constants::TEST_CONSTANTS;

use super::*;
use chia_bls::Signature;
use chia_protocol::{Coin, CoinSpend, Program};
use hex_literal::hex;

#[test]
fn test_get_conditions_from_spendbundle() {
let test_coin = Coin::new(
hex!("4444444444444444444444444444444444444444444444444444444444444444").into(),
hex!("3333333333333333333333333333333333333333333333333333333333333333").into(),
1,
);

let solution = hex!("ffff31ffb0997cc43ed8788f841fcf3071f6f212b89ba494b6ebaf1bda88c3f9de9d968a61f3b7284a5ee13889399ca71a026549a2ff8568656c6c6f8080").to_vec();
arvidn marked this conversation as resolved.
Show resolved Hide resolved
// ((49 0x997cc43ed8788f841fcf3071f6f212b89ba494b6ebaf1bda88c3f9de9d968a61f3b7284a5ee13889399ca71a026549a2 "hello"))

let spend = CoinSpend::new(test_coin, Program::new(vec![1_u8].into()), solution.into());

let coin_spends: Vec<CoinSpend> = vec![spend];
let spend_bundle = SpendBundle {
coin_spends,
aggregated_signature: Signature::default(),
};
let osbc = get_conditions_from_spendbundle(
&spend_bundle,
TEST_CONSTANTS.max_block_cost_clvm,
236,
&TEST_CONSTANTS,
)
.expect("test should pass");

assert!(osbc.spends.len() == 1);
assert!(osbc.agg_sig_unsafe.len() == 1);
}
}
73 changes: 73 additions & 0 deletions crates/chia-consensus/src/spendbundle_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use clvmr::{ENABLE_BLS_OPS_OUTSIDE_GUARD, ENABLE_FIXED_DIV};

use crate::consensus_constants::ConsensusConstants;
use crate::gen::flags::{ALLOW_BACKREFS, DISALLOW_INFINITY_G1, ENABLE_MESSAGE_CONDITIONS};

pub fn get_flags_for_height_and_constants(height: u32, constants: &ConsensusConstants) -> u32 {
let mut flags: u32 = 0;

if height >= constants.soft_fork4_height {
flags |= ENABLE_MESSAGE_CONDITIONS;
}

if height >= constants.soft_fork5_height {
flags |= DISALLOW_INFINITY_G1;
}

if height >= constants.hard_fork_height {
// the hard-fork initiated with 2.0. To activate June 2024
// * costs are ascribed to some unknown condition codes, to allow for
// soft-forking in new conditions with cost
// * a new condition, SOFTFORK, is added which takes a first parameter to
// specify its cost. This allows soft-forks similar to the softfork
// operator
// * BLS operators introduced in the soft-fork (behind the softfork
// guard) are made available outside of the guard.
// * division with negative numbers are allowed, and round toward
// negative infinity
// * AGG_SIG_* conditions are allowed to have unknown additional
// arguments
// * Allow the block generator to be serialized with the improved clvm
// serialization format (with back-references)
flags = flags | ENABLE_BLS_OPS_OUTSIDE_GUARD | ENABLE_FIXED_DIV | ALLOW_BACKREFS;
}
flags
}

#[cfg(test)]
mod tests {
use super::*;
use crate::consensus_constants::TEST_CONSTANTS;

#[test]
fn test_get_flags() {
arvidn marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(
get_flags_for_height_and_constants(
TEST_CONSTANTS.soft_fork4_height - 1,
&TEST_CONSTANTS
),
33_554_592
);
assert_eq!(
get_flags_for_height_and_constants(
TEST_CONSTANTS.soft_fork5_height - 1,
&TEST_CONSTANTS
),
167_772_320
);
assert_eq!(
get_flags_for_height_and_constants(
TEST_CONSTANTS.hard_fork_height - 1,
&TEST_CONSTANTS
),
0
);
assert_eq!(
get_flags_for_height_and_constants(
TEST_CONSTANTS.soft_fork5_height + 1,
&TEST_CONSTANTS
),
436_207_776
);
}
}
13 changes: 13 additions & 0 deletions wheel/generate_type_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ def confirm_not_included_already_hashed(
proof: bytes,
) -> bool: ...

def get_name_puzzle_conditions(
arvidn marked this conversation as resolved.
Show resolved Hide resolved
spend_bundle: SpendBundle,
max_cost: int,
constants: ConsensusConstants,
height: int,
) -> SpendBundleConditions: ...

def get_flags_for_height_and_constants(
height: int,
constants: ConsensusConstants
) -> int: ...


NO_UNKNOWN_CONDS: int = ...
STRICT_ARGS_COUNT: int = ...
LIMIT_HEAP: int = ...
Expand Down
13 changes: 13 additions & 0 deletions wheel/python/chia_rs/chia_rs.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ def confirm_not_included_already_hashed(
proof: bytes,
) -> bool: ...

def get_name_puzzle_conditions(
spend_bundle: SpendBundle,
max_cost: int,
constants: ConsensusConstants,
height: int,
) -> SpendBundleConditions: ...

def get_flags_for_height_and_constants(
height: int,
constants: ConsensusConstants
) -> int: ...


NO_UNKNOWN_CONDS: int = ...
STRICT_ARGS_COUNT: int = ...
LIMIT_HEAP: int = ...
Expand Down
31 changes: 30 additions & 1 deletion wheel/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use chia_consensus::gen::solution_generator::solution_generator as native_soluti
use chia_consensus::gen::solution_generator::solution_generator_backrefs as native_solution_generator_backrefs;
use chia_consensus::merkle_set::compute_merkle_set_root as compute_merkle_root_impl;
use chia_consensus::merkle_tree::{validate_merkle_proof, MerkleSet};
use chia_consensus::spendbundle_conditions::get_conditions_from_spendbundle;
use chia_consensus::spendbundle_validation::get_flags_for_height_and_constants;
use chia_protocol::{
BlockRecord, Bytes32, ChallengeBlockInfo, ChallengeChainSubSlot, ClassgroupElement, Coin,
CoinSpend, CoinState, CoinStateFilters, CoinStateUpdate, EndOfSubSlotBundle, Foliage,
Expand Down Expand Up @@ -41,7 +43,7 @@ use chia_protocol::{
use clvm_utils::tree_hash_from_bytes;
use clvmr::{ENABLE_BLS_OPS_OUTSIDE_GUARD, ENABLE_FIXED_DIV, LIMIT_HEAP, NO_UNKNOWN_OPS};
use pyo3::buffer::PyBuffer;
use pyo3::exceptions::{PyRuntimeError, PyValueError};
use pyo3::exceptions::{PyRuntimeError, PyTypeError, PyValueError};
use pyo3::prelude::*;
use pyo3::pybacked::PyBackedBytes;
use pyo3::types::PyBytes;
Expand Down Expand Up @@ -356,6 +358,29 @@ fn fast_forward_singleton<'p>(
))
}

#[pyfunction]
#[pyo3(name = "get_conditions_from_spendbundle")]
pub fn py_get_conditions_from_spendbundle(
spend_bundle: &SpendBundle,
max_cost: u64,
constants: &ConsensusConstants,
height: u32,
) -> PyResult<OwnedSpendBundleConditions> {
let osbc = get_conditions_from_spendbundle(spend_bundle, max_cost, height, constants).map_err(
|e| {
let error_code: u32 = e.1.into();
PyErr::new::<PyTypeError, _>(error_code)
},
)?;
Ok(osbc)
}

#[pyfunction]
#[pyo3(name = "get_flags_for_height_and_constants")]
pub fn py_get_flags_for_height_and_constants(height: u32, constants: &ConsensusConstants) -> u32 {
get_flags_for_height_and_constants(height, constants)
}

#[pymodule]
pub fn chia_rs(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
// generator functions
Expand Down Expand Up @@ -385,6 +410,10 @@ pub fn chia_rs(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(confirm_included_already_hashed, m)?)?;
m.add_function(wrap_pyfunction!(confirm_not_included_already_hashed, m)?)?;

// multithread validattion
m.add_function(wrap_pyfunction!(py_get_conditions_from_spendbundle, m)?)?;
m.add_function(wrap_pyfunction!(py_get_flags_for_height_and_constants, m)?)?;

// clvm functions
m.add("NO_UNKNOWN_CONDS", NO_UNKNOWN_CONDS)?;
m.add("STRICT_ARGS_COUNT", STRICT_ARGS_COUNT)?;
Expand Down
Loading