diff --git a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs index ee7118e0a..8b2b43aa0 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/run-generator.rs @@ -49,6 +49,8 @@ fuzz_target!(|data: &[u8]| { } (Ok(a), Ok(b)) => { assert!(a.cost >= b.cost); + assert!(a.execution_cost > b.execution_cost); + assert_eq!(a.condition_cost, b.condition_cost); assert_eq!(a.reserve_fee, b.reserve_fee); assert_eq!(a.removal_amount, b.removal_amount); assert_eq!(a.addition_amount, b.addition_amount); diff --git a/crates/chia-consensus/src/gen/conditions.rs b/crates/chia-consensus/src/gen/conditions.rs index 1ef452fab..39213398a 100644 --- a/crates/chia-consensus/src/gen/conditions.rs +++ b/crates/chia-consensus/src/gen/conditions.rs @@ -715,6 +715,12 @@ pub struct SpendBundleConditions { // the total cost) pub cost: u64, + // the cost of executing the Chialisp + pub execution_cost: u64, + + // the cost of the conditions + pub condition_cost: u64, + // the sum of all values of all spent coins pub removal_amount: u128, @@ -921,6 +927,7 @@ pub fn parse_conditions( return Err(ValidationErr(c, ErrorCode::CostExceeded)); } *max_cost -= CREATE_COIN_COST; + ret.condition_cost += CREATE_COIN_COST; } AGG_SIG_UNSAFE | AGG_SIG_ME @@ -934,6 +941,7 @@ pub fn parse_conditions( return Err(ValidationErr(c, ErrorCode::CostExceeded)); } *max_cost -= AGG_SIG_COST; + ret.condition_cost += AGG_SIG_COST; } _ => (), } @@ -1212,6 +1220,7 @@ pub fn parse_conditions( return Err(ValidationErr(c, ErrorCode::CostExceeded)); } *max_cost -= cost; + ret.condition_cost += cost; } Condition::SendMessage(src_mode, dst, msg) => { decrement(&mut announce_countdown, msg)?; diff --git a/crates/chia-consensus/src/gen/owned_conditions.rs b/crates/chia-consensus/src/gen/owned_conditions.rs index f39b8f25e..ac2c3eab1 100644 --- a/crates/chia-consensus/src/gen/owned_conditions.rs +++ b/crates/chia-consensus/src/gen/owned_conditions.rs @@ -70,6 +70,8 @@ pub struct OwnedSpendBundleConditions { // set if the aggregate signature of the block/spend bundle was // successfully validated pub validated_signature: bool, + pub execution_cost: u64, + pub condition_cost: u64, } impl OwnedSpendConditions { @@ -144,6 +146,8 @@ impl OwnedSpendBundleConditions { removal_amount: sb.removal_amount, addition_amount: sb.addition_amount, validated_signature: sb.validated_signature, + execution_cost: sb.execution_cost, + condition_cost: sb.condition_cost, } } } diff --git a/crates/chia-consensus/src/gen/run_block_generator.rs b/crates/chia-consensus/src/gen/run_block_generator.rs index 164262b96..0f52fbe2c 100644 --- a/crates/chia-consensus/src/gen/run_block_generator.rs +++ b/crates/chia-consensus/src/gen/run_block_generator.rs @@ -126,6 +126,7 @@ where constants, )?; result.cost += max_cost - cost_left; + result.execution_cost = clvm_cost; Ok(result) } @@ -189,13 +190,16 @@ where let Reduction(clvm_cost, mut all_spends) = run_program(a, &dialect, program, args, cost_left)?; subtract_cost(a, &mut cost_left, clvm_cost)?; + + let mut ret = SpendBundleConditions::default(); + all_spends = first(a, all_spends)?; + ret.execution_cost += clvm_cost; // at this point all_spends is a list of: // (parent-coin-id puzzle-reveal amount solution . extra) // where extra may be nil, or additional extension data - let mut ret = SpendBundleConditions::default(); let mut state = ParseState::default(); let mut cache = HashMap::::new(); @@ -209,6 +213,7 @@ where run_program(a, &dialect, puzzle, solution, cost_left)?; subtract_cost(a, &mut cost_left, clvm_cost)?; + ret.execution_cost += clvm_cost; let buf = tree_hash_cached(a, puzzle, &backrefs, &mut cache); let puzzle_hash = a.new_atom(&buf)?; diff --git a/crates/chia-consensus/src/spendbundle_conditions.rs b/crates/chia-consensus/src/spendbundle_conditions.rs index 236cf33a3..4e3d406d3 100644 --- a/crates/chia-consensus/src/spendbundle_conditions.rs +++ b/crates/chia-consensus/src/spendbundle_conditions.rs @@ -71,6 +71,7 @@ pub fn run_spendbundle( let amount = a.new_number(coin_spend.coin.amount.into())?; let Reduction(clvm_cost, conditions) = run_program(a, &dialect, puz, sol, cost_left)?; + ret.execution_cost += clvm_cost; subtract_cost(a, &mut cost_left, clvm_cost)?; let buf = tree_hash(a, puz); @@ -169,6 +170,12 @@ mod tests { conditions.cost, block_conds.cost - QUOTE_EXECUTION_COST - QUOTE_BYTES_COST ); + + assert_eq!( + conditions.execution_cost, + block_conds.execution_cost - QUOTE_EXECUTION_COST + ); + assert_eq!(conditions.condition_cost, block_conds.condition_cost); } #[rstest] @@ -382,10 +389,18 @@ mod tests { - QUOTE_BYTES; let bundle_byte_cost = generator_length_without_quote as u64 * TEST_CONSTANTS.cost_per_byte; - println!("block_cost: {block_cost} bytes: {block_byte_cost}"); + println!(" block_cost: {block_cost} bytes: {block_byte_cost}"); println!("bundle_cost: {} bytes: {bundle_byte_cost}", conditions.cost); + println!("execution_cost: {}", conditions.execution_cost); + println!("condition_cost: {}", conditions.condition_cost); assert!(conditions.cost - bundle_byte_cost <= block_cost - block_byte_cost); assert!(conditions.cost > 0); + assert!(conditions.execution_cost > 0); + assert!(conditions.condition_cost > 0); + assert_eq!( + conditions.cost, + conditions.condition_cost + conditions.execution_cost + bundle_byte_cost + ); // update the cost we print here, just to be compatible with // the test cases we have. We've already ensured the cost is // lower diff --git a/tests/test_run_block_generator.py b/tests/test_run_block_generator.py index ec13346a8..a16bc4f5e 100644 --- a/tests/test_run_block_generator.py +++ b/tests/test_run_block_generator.py @@ -15,9 +15,16 @@ def test_run_block_generator_cost() -> None: # longer pay the cost of the generator ROM hard_fork_consensus_cost = 596498808 + # the generator always produce the same set of conditions, and their cost + # is the same regardless of pre- or post- hard fork. + condition_cost = 122400000 + generator = bytes.fromhex( open("generator-tests/block-834768.txt", "r").read().split("\n")[0] ) + + byte_cost = len(generator) * 12000 + err, conds = run_block_generator( generator, [], @@ -29,6 +36,9 @@ def test_run_block_generator_cost() -> None: ) assert err is None assert conds is not None + assert conds.cost == original_consensus_cost + assert conds.execution_cost + condition_cost + byte_cost == original_consensus_cost + assert conds.condition_cost == condition_cost err2, conds2 = run_block_generator2( generator, @@ -41,6 +51,9 @@ def test_run_block_generator_cost() -> None: ) assert err2 is None assert conds2 is not None + assert conds2.cost == hard_fork_consensus_cost + assert conds2.condition_cost == condition_cost + assert conds2.execution_cost + condition_cost + byte_cost == hard_fork_consensus_cost output1 = print_spend_bundle_conditions(conds) output2 = print_spend_bundle_conditions(conds2) @@ -81,7 +94,7 @@ def test_run_block_generator_cost() -> None: err, conds = run_block_generator( generator, [], - len(generator) * 12000 - 1, + byte_cost - 1, DONT_VALIDATE_SIGNATURE, G2Element(), None, @@ -95,7 +108,7 @@ def test_run_block_generator_cost() -> None: err, conds = run_block_generator2( generator, [], - len(generator) * 12000 - 1, + byte_cost - 1, DONT_VALIDATE_SIGNATURE, G2Element(), None, diff --git a/tests/test_streamable.py b/tests/test_streamable.py index cc9b30c0b..240080809 100644 --- a/tests/test_streamable.py +++ b/tests/test_streamable.py @@ -85,10 +85,34 @@ 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, False + [], + 1000, + 1337, + 42, + None, + None, + [(pk, b"msg")], + 12345678, + 123, + 456, + False, + 4321, + 8765, ) a2 = SpendBundleConditions( - [], 1001, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False + [], + 1001, + 1337, + 42, + None, + None, + [(pk, b"msg")], + 12345678, + 123, + 456, + False, + 4321, + 8765, ) b = hash(a1) c = hash(a2) @@ -391,7 +415,19 @@ 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, False + [], + 1000, + 1337, + 42, + None, + None, + [(pk, b"msg")], + 12345678, + 123, + 456, + False, + 4321, + 8765, ) assert a.to_json_dict() == { @@ -406,13 +442,27 @@ def test_json_spend_bundle_conditions() -> None: "removal_amount": 123, "addition_amount": 456, "validated_signature": False, + "execution_cost": 4321, + "condition_cost": 8765, } def test_from_json_spend_bundle_conditions() -> None: a = SpendBundleConditions( - [], 1000, 1337, 42, None, None, [(pk, b"msg")], 12345678, 123, 456, False + [], + 1000, + 1337, + 42, + None, + None, + [(pk, b"msg")], + 12345678, + 123, + 456, + False, + 4321, + 8765, ) b = SpendBundleConditions.from_json_dict( { @@ -427,6 +477,8 @@ def test_from_json_spend_bundle_conditions() -> None: "removal_amount": 123, "addition_amount": 456, "validated_signature": False, + "execution_cost": 4321, + "condition_cost": 8765, } ) assert a == b @@ -468,7 +520,19 @@ 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, False + [], + 1000, + 1337, + 42, + None, + None, + [(pk, b"msg")], + 12345678, + 123, + 456, + False, + 4321, + 8765, ) b = copy.copy(a) diff --git a/wheel/generate_type_stubs.py b/wheel/generate_type_stubs.py index 188071eb0..bd9e06824 100644 --- a/wheel/generate_type_stubs.py +++ b/wheel/generate_type_stubs.py @@ -514,6 +514,8 @@ def __init__( "removal_amount: int", "addition_amount: int", "validated_signature: bool", + "execution_cost: int", + "condition_cost: int", ], ) diff --git a/wheel/python/chia_rs/chia_rs.pyi b/wheel/python/chia_rs/chia_rs.pyi index 34fa2efdd..f7851ed3d 100644 --- a/wheel/python/chia_rs/chia_rs.pyi +++ b/wheel/python/chia_rs/chia_rs.pyi @@ -350,6 +350,8 @@ class SpendBundleConditions: removal_amount: int addition_amount: int validated_signature: bool + execution_cost: int + condition_cost: int def __init__( self, spends: Sequence[SpendConditions], @@ -362,7 +364,9 @@ class SpendBundleConditions: cost: int, removal_amount: int, addition_amount: int, - validated_signature: bool + validated_signature: bool, + execution_cost: int, + condition_cost: int ) -> None: ... def __hash__(self) -> int: ... def __repr__(self) -> str: ... @@ -391,7 +395,9 @@ class SpendBundleConditions: cost: Union[ int, _Unspec] = _Unspec(), removal_amount: Union[ int, _Unspec] = _Unspec(), addition_amount: Union[ int, _Unspec] = _Unspec(), - validated_signature: Union[ bool, _Unspec] = _Unspec()) -> SpendBundleConditions: ... + validated_signature: Union[ bool, _Unspec] = _Unspec(), + execution_cost: Union[ int, _Unspec] = _Unspec(), + condition_cost: Union[ int, _Unspec] = _Unspec()) -> SpendBundleConditions: ... @final class BlockRecord: