From 624aecf98eb5f1c8f045201e93b88a70f71c1a8c Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Thu, 24 Sep 2026 21:01:03 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix(executor):=20correct=20fee=20reserves?= =?UTF-8?q?=20and=20forward=20opaque=20subtrees=20=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- executor/crates/common/tests/fees_abi.rs | 266 +++--------- executor/install/config/genvm.yaml | 10 +- executor/src/wasi/genlayer_sdk/message.rs | 82 ++-- executor/src/wasi/genlayer_sdk/tests.rs | 397 +++++++++++++++--- executor/tests/message_fee_time_units.rs | 32 +- .../balance/balance/balance.0_0.hash | 2 +- .../balance/balance/balance.0_0.stdout | 2 +- .../sandbox_overspend.0.hash | 2 +- .../sandbox_overspend.0.stdout | 2 +- .../sandbox_overspend_2.0.hash | 2 +- .../sandbox_overspend_2.0.stdout | 2 +- .../integration/message/deploy/deploy.0.hash | 2 +- .../message/deploy/deploy.0.stdout | 2 +- .../message/deploy_salt/deploy_salt.0.hash | 2 +- .../message/deploy_salt/deploy_salt.0.stdout | 2 +- .../internal_below_min_timeunits.jsonnet | 3 +- .../message_count_cap.0.hash | 2 +- .../message_count_cap.0.stdout | 2 +- .../message_count_cap.2.hash | 2 +- .../message_count_cap.2.stdout | 2 +- .../message_count_cap.3.hash | 2 +- .../message_count_cap.4.hash | 2 +- .../message_count_cap.4.stdout | 2 +- .../message_count_cap.5.hash | 2 +- .../message_count_cap.jsonnet | 12 +- .../nested_allocation_budget.0.hash | 2 +- .../nested_allocation_budget.0.stdout | 2 +- .../nested_allocation_budget.jsonnet | 18 +- .../message/send_message/send_message.0.hash | 2 +- .../send_message/send_message.0.stdout | 2 +- .../send_message_on/send_message_on.0_0.hash | 2 +- .../send_message_on.0_0.stdout | 2 +- .../use_balance_no_alloc.0_0_0.hash | 2 +- .../use_balance_no_alloc.0_0_0.stdout | 2 +- .../use_balance_ok/use_balance_ok.0_0.hash | 2 +- .../use_balance_ok/use_balance_ok.0_0.stdout | 2 +- .../use_balance_on_deploy.0.hash | 2 +- .../use_balance_on_deploy.0.stdout | 2 +- .../use_balance_sandbox.0_0.hash | 2 +- .../use_balance_sandbox.0_0.stdout | 2 +- .../use_balance_scaled.0.hash | 2 +- .../use_balance_scaled.0_0.hash | 2 +- .../use_balance_scaled.0_0.stdout | 2 +- .../use_balance_scaled/use_balance_scaled.py | 8 +- .../use_balance_zero_budget.0_0.hash | 2 +- .../use_balance_zero_budget.0_0.stdout | 2 +- 46 files changed, 534 insertions(+), 368 deletions(-) diff --git a/executor/crates/common/tests/fees_abi.rs b/executor/crates/common/tests/fees_abi.rs index 1c31f659..27d51a3e 100644 --- a/executor/crates/common/tests/fees_abi.rs +++ b/executor/crates/common/tests/fees_abi.rs @@ -1,230 +1,62 @@ +use genlayer_sdk::calldata::{self, codec::Decode as _}; use genvm_modules_interfaces::fees::{ - ExternalMessageParams, InternalMessageParams, MessageAllocationNode, - MessageAllocationNodeParams, + InternalMessageParams, MessageAllocationNode, MessageAllocationNodeParams, }; use primitive_types::U256; -/// Concatenates 32-byte big-endian ABI words into one buffer. -fn words(values: &[U256]) -> Vec { - let mut buf = Vec::with_capacity(values.len() * 32); - for v in values { - buf.extend_from_slice(&v.to_big_endian()); - } - buf -} - -/// Reads the `i`-th 32-byte ABI word from an encoded buffer. -fn word(buf: &[u8], i: usize) -> U256 { - U256::from_big_endian(&buf[i * 32..i * 32 + 32]) -} - -fn external_node( - recipient: Option<[u8; 20]>, - call_key: Option<[u8; 32]>, - budget: u64, - gas_limit: u64, - max_gas_price: u64, - children: Vec, -) -> MessageAllocationNode { - MessageAllocationNode { - recipient: recipient.map(genlayer_sdk::calldata::Address::from), - call_key: call_key.map(genvm_modules_interfaces::CallKey), - budget: U256::from(budget), - // External messages have no acceptance/finalize lifecycle; value is unused. - on: genvm_modules_interfaces::On::Finalized, - fee_params: MessageAllocationNodeParams::External(ExternalMessageParams { - gas_limit: U256::from(gas_limit), - max_gas_price: U256::from(max_gas_price), - }), - children, - } -} - -fn internal_node( - on: genvm_modules_interfaces::On, - budget: u64, - rotations: &[u64], - children: Vec, -) -> MessageAllocationNode { - MessageAllocationNode { +#[test] +fn flat_allocation_preserves_opaque_payload_and_requires_descendant_budget() { + let allocation = MessageAllocationNode { recipient: None, call_key: None, - budget: U256::from(budget), - on, + budget: Some(U256::from(100)), + on: genvm_modules_interfaces::On::Decided, fee_params: MessageAllocationNodeParams::Internal(std::sync::Arc::new( InternalMessageParams { - leader_timeunits_allocation: U256::from(1), - validator_timeunits_allocation: U256::from(2), - execution_budget_per_round: U256::from(3), - rotations: rotations.iter().map(|r| U256::from(*r)).collect(), - max_price_gen_per_time_unit: U256::from(11), - storage_fee_max_gas_price: U256::from(12), - receipt_fee_max_gas_price: U256::from(13), + leader_timeunits_allocation: U256::one(), + validator_timeunits_allocation: U256::one(), + execution_budget_per_round: U256::one(), + rotations: vec![U256::zero()], + max_price_gen_per_time_unit: U256::one(), + storage_fee_max_gas_price: U256::one(), + receipt_fee_max_gas_price: U256::one(), }, )), - children, + children_budget: U256::from(60), + subtree: vec![0, 255, 7, 0, 128].into(), + }; + let value = calldata::to_value(&allocation); + let decoded = + MessageAllocationNode::decode(calldata::codec::ValueDeserializer(value.clone())).unwrap(); + assert_eq!(decoded.children_budget, allocation.children_budget); + assert_eq!(decoded.subtree, allocation.subtree); + assert_eq!(decoded.budget, allocation.budget); + + for budget in [Some(U256::zero()), None] { + let mut allocation = allocation.clone(); + allocation.budget = budget; + let decoded = MessageAllocationNode::decode(calldata::codec::ValueDeserializer( + calldata::to_value(&allocation), + )) + .unwrap(); + assert_eq!(decoded.budget, budget); + let decoded: MessageAllocationNode = + serde_json::from_value(serde_json::to_value(&allocation).unwrap()).unwrap(); + assert_eq!(decoded.budget, budget); } -} - -// -- Exact wire layout ----------------------------------------------- - -#[test] -fn external_root_node_matches_exact_encoding() { - let recipient = [0x11u8; 20]; - let root = external_node(Some(recipient), None, 5, 7, 9, vec![]); - let encoded = root.abi_encode(); - - // `abi.encode(MessageAllocationNode[])`: array offset, length, element - // offset, then the 10-word element tuple - // (messageType=External, onAcceptance=false, parent=sentinel, recipient, - // callKey wildcard, budget, feeParams offset, feeParams len, gasLimit, maxGasPrice). - let expected = words(&[ - U256::from(0x20), // offset to array - U256::from(1), // array length - U256::from(0x20), // element[0] head offset - U256::from(0), // messageType = External - U256::from(0), // onAcceptance = false - U256::MAX, // parentIndex = NODE_ROOT_SENTINEL - U256::from_big_endian(&recipient), // recipient (left-padded) - U256::from_big_endian(&genvm_modules_interfaces::fees::CALL_KEY_WILDCARD.0), - U256::from(5), // budget - U256::from(0xE0), // feeParams offset (7 head words) - U256::from(64), // feeParams bytes length - U256::from(7), // gasLimit - U256::from(9), // maxGasPrice - ]); - - assert_eq!(encoded, expected); -} - -// -- Tree flattening to parent-pointer form -------------------------- - -#[test] -fn nested_internal_flattens_with_parent_pointers() { - let grandchild = external_node(Some([0x44u8; 20]), None, 1, 100, 200, vec![]); - let first_child = external_node(Some([0x22u8; 20]), None, 2, 100, 200, vec![grandchild]); - let second_child = external_node(Some([0x33u8; 20]), None, 3, 100, 200, vec![]); - let root = internal_node( - genvm_modules_interfaces::On::Decided, - 10, - &[2, 3], - vec![first_child, second_child], - ); - - let encoded = root.abi_encode(); - - assert_eq!(word(&encoded, 0), U256::from(0x20)); - assert_eq!(word(&encoded, 1), U256::from(4), "four flattened nodes"); - - // Heads region begins right after the array length word, and the - // per-element offsets there are relative to it. - let heads_base = 2 * 32; - let element_idx = |index: usize| (heads_base + word(&encoded, 2 + index).as_usize()) / 32; - let root_idx = element_idx(0); - let first_child_idx = element_idx(1); - let second_child_idx = element_idx(2); - let grandchild_idx = element_idx(3); - - // Root: messageType Internal (1), onAcceptance true, parent = sentinel. - assert_eq!( - word(&encoded, root_idx), - U256::from(1), - "root messageType Internal" - ); - assert_eq!( - word(&encoded, root_idx + 1), - U256::one(), - "root onAcceptance" - ); - assert_eq!( - word(&encoded, root_idx + 2), - U256::MAX, - "root parent = sentinel" - ); - // Both children precede the grandchild in BFS order. - assert_eq!( - word(&encoded, first_child_idx + 2), - U256::zero(), - "first child parent index 0" - ); - assert_eq!( - word(&encoded, second_child_idx + 2), - U256::zero(), - "second child parent index 0" - ); - assert_eq!( - word(&encoded, grandchild_idx + 2), - U256::one(), - "grandchild parent index 1" - ); -} - -// -- Chain-derived feeParams fields ---------------------------------- - -#[test] -fn internal_params_encode_derived_appeal_rounds() { - // appealRounds is not stored on the Rust side; it is reconstructed as - // len(rotations) - 1 when encoding. - let root = internal_node( - genvm_modules_interfaces::On::Finalized, - 10, - &[2, 3, 4], - vec![], - ); - let encoded = root.abi_encode(); - - // Walk to the feeParams bytes inside the single element. - let heads_base = 2 * 32; - let elem_idx = (heads_base + word(&encoded, 2).as_usize()) / 32; - // element head is 7 words; the feeParams offset word is the 7th (elem_idx + 6). - let fee_params_len_idx = elem_idx + word(&encoded, elem_idx + 6).as_usize() / 32; - // feeParams = [len][ abi.encode(InternalMessageParams) ], tuple starts after len. - let inner_base = fee_params_len_idx + 1; - - // InternalMessageParams tuple (v0.6-dev, 8 head words): - // [offset][leader][validator][appealRounds][exec][rot off][maxPriceGen] - // [storageCap][receiptCap][rot len][rot...] - assert_eq!( - word(&encoded, inner_base), - U256::from(0x20), - "struct offset" - ); - assert_eq!(word(&encoded, inner_base + 1), U256::from(1), "leader"); - assert_eq!(word(&encoded, inner_base + 2), U256::from(2), "validator"); - assert_eq!( - word(&encoded, inner_base + 3), - U256::from(2), - "appealRounds = 3 - 1" - ); - assert_eq!( - word(&encoded, inner_base + 4), - U256::from(3), - "execBudgetPerRound" - ); - assert_eq!( - word(&encoded, inner_base + 5), - U256::from(0x100), - "rotations offset (8 head words)" - ); - assert_eq!( - word(&encoded, inner_base + 6), - U256::from(11), - "maxPriceGenPerTimeUnit" - ); - assert_eq!( - word(&encoded, inner_base + 7), - U256::from(12), - "storageFeeMaxGasPrice" - ); - assert_eq!( - word(&encoded, inner_base + 8), - U256::from(13), - "receiptFeeMaxGasPrice" - ); - assert_eq!( - word(&encoded, inner_base + 9), - U256::from(3), - "rotations length" - ); + for field in ["children_budget", "subtree"] { + let calldata::Value::Map(mut fields) = value.clone() else { + panic!("allocation must encode as a map") + }; + fields.remove(field); + let error = MessageAllocationNode::decode(calldata::codec::ValueDeserializer( + calldata::Value::Map(fields), + )) + .unwrap_err(); + assert!( + error.to_string().contains(field), + "unexpected error: {error}" + ); + } } diff --git a/executor/install/config/genvm.yaml b/executor/install/config/genvm.yaml index e9e06282..b90218d3 100644 --- a/executor/install/config/genvm.yaml +++ b/executor/install/config/genvm.yaml @@ -127,8 +127,14 @@ fees: else let overlaySplit = idiv (timeUnitPool * node.overlaySplitBps) (10000 - node.overlaySplitBps) in - # 7. minimum primary fees - let minPrimaryFees = timeUnitPool + overlaySplit + executionTerm in + # 7. Successful appeals return the bond plus floor(1.5 * bond) profit + let appealProfitReserve = foldRange 1 (appealRounds + 1) 0 (\acc i = + let bond = (arrayGetElem rotations i + 1) + * (leaderTU + arrayGetElem validatorsPerRound (2 * i) * validatorTU) in + let pricedBond = if multiplier > 0 then multiplier * bond else bond in + acc + pricedBond + idiv pricedBond 2) in + # The appeal-profit reserve is not subject to the developer/DAO overlay + let minPrimaryFees = timeUnitPool + overlaySplit + executionTerm + appealProfitReserve in minPrimaryFees in storage: diff --git a/executor/src/wasi/genlayer_sdk/message.rs b/executor/src/wasi/genlayer_sdk/message.rs index 9c317c1e..d6cc716a 100644 --- a/executor/src/wasi/genlayer_sdk/message.rs +++ b/executor/src/wasi/genlayer_sdk/message.rs @@ -19,18 +19,6 @@ fn allocation_match_priority( Some(recipient_priority + call_key_priority) } -fn internal_allocation_match_priority( - node: &genvm_modules_interfaces::fees::MessageAllocationNode, - recipient: calldata::Address, - call_key: genvm_modules_interfaces::abi_stub::CallKey, -) -> Option { - if node.budget.is_zero() { - return None; - } - - allocation_match_priority(node, recipient, call_key) -} - pub(super) fn resolve_internal_allocation( nodes: &[genvm_modules_interfaces::fees::MessageAllocationNode], on: genvm_modules_interfaces::On, @@ -48,11 +36,11 @@ pub(super) fn resolve_internal_allocation( genvm_modules_interfaces::fees::MessageAllocationNodeParams::Internal(_) ) }) - .filter_map(|node| internal_allocation_match_priority(node, recipient, call_key)) + .filter_map(|node| allocation_match_priority(node, recipient, call_key)) .min()?; let index = nodes.iter().position(|node| { - internal_allocation_match_priority(node, recipient, call_key) == Some(priority) - && node.on == on + allocation_match_priority(node, recipient, call_key) == Some(priority) + && (node.recipient.is_some() || node.on == on) && matches!( &node.fee_params, genvm_modules_interfaces::fees::MessageAllocationNodeParams::Internal(_) @@ -163,18 +151,16 @@ async fn consume_message_fee_internal( fee_params: Arc, args: ConsumeInternalArgs, ) -> Result { + validate_internal_price_caps(&fee_params)?; let mut fee_cost = shared_data .data_fees_limit .calculate_message_fee_internal(&fee_params) .map_err(internal_trap)?; if let FeeFunding::Allocation { node, .. } = &funding { - let declared_budget = node - .children - .iter() - .try_fold(fee_cost.reported_fee(), |total, child| { - total.checked_add(child.budget) - }) + let declared_budget = fee_cost + .reported_fee() + .checked_add(node.children_budget) .ok_or_else(|| { internal_trap(rt::errors::internal!("message declared budget overflow")) })?; @@ -199,11 +185,15 @@ async fn consume_message_fee_internal( match funding { FeeFunding::Allocation { node, consumed } => { - let remaining_budget = node.budget.checked_sub(*consumed).ok_or_else(|| { - internal_trap(rt::errors::internal!( - "message allocation consumed budget exceeds its total" - )) - })?; + let remaining_budget = node + .budget + .unwrap_or(primitive_types::U256::MAX) + .checked_sub(*consumed) + .ok_or_else(|| { + internal_trap(rt::errors::internal!( + "message allocation consumed budget exceeds its total" + )) + })?; if fee_total > remaining_budget { log_warn!( node:cd = *node, @@ -310,11 +300,15 @@ async fn consume_message_fee_external( .map_err(internal_trap)?; let fee_total = fee_cost.reported_fee(); - let remaining_budget = node.budget.checked_sub(*consumed).ok_or_else(|| { - internal_trap(rt::errors::internal!( - "message allocation consumed budget exceeds its total" - )) - })?; + let remaining_budget = node + .budget + .unwrap_or(primitive_types::U256::MAX) + .checked_sub(*consumed) + .ok_or_else(|| { + internal_trap(rt::errors::internal!( + "message allocation consumed budget exceeds its total" + )) + })?; if fee_total > remaining_budget { return Err(internal_trap(rt::errors::Error::vm( abi::consts::VmError::out_of() @@ -407,6 +401,19 @@ async fn consume_external_receipt_only( pub(super) const FEE_PARAM_PRICE_BITS: usize = 96; pub(super) const FEE_PARAM_COUNT_BITS: usize = 32; +fn validate_internal_price_caps( + params: &abi::fees::InternalMessageParams, +) -> Result<(), generated::types::Error> { + if params.max_price_gen_per_time_unit.is_zero() + || params.storage_fee_max_gas_price.is_zero() + || params.receipt_fee_max_gas_price.is_zero() + { + log_debug!("internal message rejected: zero price cap (Inval)"); + return Err(generated::types::Errno::Inval.into()); + } + Ok(()) +} + /// Validates the balance-funded fee fields (`use_balance` / `fee_params`) shared /// by `EmitInternalMessage` and `EmitInternalDeployMessage`. Returns the fee params /// to meter against the contract balance when `use_balance` is set, or `None` @@ -442,13 +449,7 @@ pub(super) fn validate_balance_fee( log_debug!("balance-funded message rejected: rotations empty (Inval)"); return Err(generated::types::Errno::Inval.into()); } - if params.max_price_gen_per_time_unit.is_zero() - || params.storage_fee_max_gas_price.is_zero() - || params.receipt_fee_max_gas_price.is_zero() - { - log_debug!("balance-funded message rejected: zero price cap (Inval)"); - return Err(generated::types::Errno::Inval.into()); - } + validate_internal_price_caps(¶ms)?; let too_large = params.max_price_gen_per_time_unit.bits() > FEE_PARAM_PRICE_BITS || params.storage_fee_max_gas_price.bits() > FEE_PARAM_PRICE_BITS || params.receipt_fee_max_gas_price.bits() > FEE_PARAM_PRICE_BITS @@ -541,6 +542,7 @@ impl ContextVFS<'_> { .map_err(internal_trap)?; let remaining_budget = node .budget + .unwrap_or(primitive_types::U256::MAX) .checked_sub( self.context .data @@ -877,7 +879,7 @@ impl ContextVFS<'_> { let fee_params = convert_internal_message_params_to_sdk(matched_params.as_ref()); let accumulator = &mut self.context.data.accumulator; let matched_node = &accumulator.message_fee_allocation[matched_index]; - let subtree = bytes::Bytes::from(matched_node.abi_encode()); + let subtree = matched_node.subtree.clone(); let rotations_size = usize_into_u64(fee_params.rotations.len()) .saturating_mul(memory_limiter_consts::MESSAGE_FEE_ROTATION_ELEMENT_SIZE.into()); let allocation = reserve_permanent( @@ -1079,7 +1081,7 @@ impl ContextVFS<'_> { let fee_params = convert_internal_message_params_to_sdk(matched_params.as_ref()); let accumulator = &mut self.context.data.accumulator; let matched_node = &accumulator.message_fee_allocation[matched_index]; - let subtree = bytes::Bytes::from(matched_node.abi_encode()); + let subtree = matched_node.subtree.clone(); let rotations_size = usize_into_u64(fee_params.rotations.len()) .saturating_mul(memory_limiter_consts::MESSAGE_FEE_ROTATION_ELEMENT_SIZE.into()); let allocation = reserve_permanent( diff --git a/executor/src/wasi/genlayer_sdk/tests.rs b/executor/src/wasi/genlayer_sdk/tests.rs index 9dc7a33c..e5c35efa 100644 --- a/executor/src/wasi/genlayer_sdk/tests.rs +++ b/executor/src/wasi/genlayer_sdk/tests.rs @@ -75,7 +75,7 @@ fn external_message_allocation() -> genvm_modules_interfaces::fees::MessageAlloc genvm_modules_interfaces::fees::MessageAllocationNode { recipient: None, call_key: None, - budget: U256::from(100), + budget: Some(U256::from(100)), on: genvm_modules_interfaces::On::Finalized, fee_params: genvm_modules_interfaces::fees::MessageAllocationNodeParams::External( genvm_modules_interfaces::fees::ExternalMessageParams { @@ -83,7 +83,8 @@ fn external_message_allocation() -> genvm_modules_interfaces::fees::MessageAlloc max_gas_price: U256::zero(), }, ), - children: Vec::new(), + children_budget: U256::zero(), + subtree: bytes::Bytes::new(), } } @@ -91,7 +92,7 @@ fn internal_message_allocation() -> genvm_modules_interfaces::fees::MessageAlloc genvm_modules_interfaces::fees::MessageAllocationNode { recipient: None, call_key: None, - budget: U256::from(100), + budget: Some(U256::from(100)), on: genvm_modules_interfaces::On::Finalized, fee_params: genvm_modules_interfaces::fees::MessageAllocationNodeParams::Internal( Arc::new(genvm_modules_interfaces::fees::InternalMessageParams { @@ -104,30 +105,21 @@ fn internal_message_allocation() -> genvm_modules_interfaces::fees::MessageAlloc receipt_fee_max_gas_price: U256::one(), }), ), - children: Vec::new(), + children_budget: U256::zero(), + subtree: bytes::Bytes::new(), } } -fn allocation_child( - budget: U256, - children: Vec, -) -> genvm_modules_interfaces::fees::MessageAllocationNode { - let mut node = internal_message_allocation(); - node.budget = budget; - node.children = children; - node -} - #[test] fn internal_allocation_prefers_exact_key_over_earlier_wildcard() { let recipient = calldata::Address::from([7; 20]); let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); let mut wildcard = internal_message_allocation(); wildcard.recipient = Some(recipient); - wildcard.budget = U256::one(); + wildcard.budget = Some(U256::one()); let mut exact = wildcard.clone(); exact.call_key = Some(call_key); - exact.budget = U256::from(2); + exact.budget = Some(U256::from(2)); let nodes = vec![wildcard, exact]; let (matched, _) = resolve_internal_allocation( @@ -138,19 +130,19 @@ fn internal_allocation_prefers_exact_key_over_earlier_wildcard() { ) .expect("exact allocation should match"); - assert_eq!(nodes[matched].budget, U256::from(2)); + assert_eq!(nodes[matched].budget, Some(U256::from(2))); } #[test] -fn internal_allocation_skips_zero_budget_exact_key() { +fn internal_allocation_keeps_exhausted_exact_key() { let recipient = calldata::Address::from([7; 20]); let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); let mut wildcard = internal_message_allocation(); wildcard.recipient = Some(recipient); - wildcard.budget = U256::one(); + wildcard.budget = Some(U256::one()); let mut exact = wildcard.clone(); exact.call_key = Some(call_key); - exact.budget = U256::zero(); + exact.budget = Some(U256::zero()); let nodes = vec![wildcard, exact]; let (matched, _) = resolve_internal_allocation( @@ -159,9 +151,51 @@ fn internal_allocation_skips_zero_budget_exact_key() { recipient, call_key, ) - .expect("wildcard allocation should match"); + .expect("exhausted exact allocation should still match"); - assert_eq!(nodes[matched].budget, U256::one()); + assert_eq!(matched, 1); + assert_eq!(nodes[matched].budget, Some(U256::zero())); +} + +#[test] +fn internal_allocation_keeps_uncapped_exact_key() { + let recipient = calldata::Address::from([7; 20]); + let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); + let wildcard = internal_message_allocation(); + let mut exact = wildcard.clone(); + exact.recipient = Some(recipient); + exact.call_key = Some(call_key); + exact.budget = None; + let nodes = vec![wildcard, exact]; + assert_eq!( + resolve_internal_allocation( + &nodes, + genvm_modules_interfaces::On::Finalized, + recipient, + call_key + ) + .unwrap() + .0, + 1 + ); +} + +#[test] +fn internal_allocation_does_not_search_duplicate_chain_keys_by_phase() { + let recipient = calldata::Address::from([7; 20]); + let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); + let mut first = internal_message_allocation(); + first.recipient = Some(recipient); + first.call_key = Some(call_key); + let mut duplicate = first.clone(); + duplicate.on = genvm_modules_interfaces::On::Decided; + assert!(resolve_internal_allocation( + &[first, duplicate], + genvm_modules_interfaces::On::Decided, + recipient, + call_key + ) + .is_none()); } #[test] @@ -189,10 +223,10 @@ fn internal_allocation_selects_phase_within_equal_keys() { let recipient = calldata::Address::from([7; 20]); let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); let mut finalized = internal_message_allocation(); - finalized.budget = U256::one(); + finalized.budget = Some(U256::one()); let mut decided = finalized.clone(); decided.on = genvm_modules_interfaces::On::Decided; - decided.budget = U256::from(2); + decided.budget = Some(U256::from(2)); let nodes = vec![finalized, decided]; let (matched, _) = resolve_internal_allocation( @@ -203,7 +237,7 @@ fn internal_allocation_selects_phase_within_equal_keys() { ) .expect("decided allocation should match"); - assert_eq!(nodes[matched].budget, U256::from(2)); + assert_eq!(nodes[matched].budget, Some(U256::from(2))); } #[test] @@ -211,13 +245,13 @@ fn external_allocation_candidates_follow_consensus_precedence() { let recipient = calldata::Address::from([7; 20]); let call_key = genvm_modules_interfaces::abi_stub::CallKey([8; 32]); let mut global_wildcard = external_message_allocation(); - global_wildcard.budget = U256::one(); + global_wildcard.budget = Some(U256::one()); let mut recipient_wildcard = global_wildcard.clone(); recipient_wildcard.recipient = Some(recipient); - recipient_wildcard.budget = U256::from(2); + recipient_wildcard.budget = Some(U256::from(2)); let mut exact = recipient_wildcard.clone(); exact.call_key = Some(call_key); - exact.budget = U256::from(3); + exact.budget = Some(U256::from(3)); let nodes = vec![global_wildcard, recipient_wildcard, exact]; let candidates = external_allocation_candidates(&nodes, recipient, call_key); @@ -226,7 +260,10 @@ fn external_allocation_candidates_follow_consensus_precedence() { .map(|index| nodes[index].budget) .collect::>(); - assert_eq!(budgets, vec![U256::from(3), U256::from(2), U256::one()]); + assert_eq!( + budgets, + vec![Some(U256::from(3)), Some(U256::from(2)), Some(U256::one())] + ); } #[test] @@ -236,7 +273,7 @@ fn external_allocation_candidates_include_zero_budget_nodes() { let mut node = external_message_allocation(); node.recipient = Some(recipient); node.call_key = Some(call_key); - node.budget = U256::zero(); + node.budget = Some(U256::zero()); let nodes = vec![node]; assert_eq!( @@ -327,6 +364,10 @@ struct EmissionTestContext { impl EmissionTestContext { fn new(memory_limit: u32, fee_total: u64) -> Self { + Self::with_fees(memory_limit, fee_total, emission_fees()) + } + + fn with_fees(memory_limit: u32, fee_total: u64, fees: crate::config::FeesConfig) -> Self { let root = TestDir::new(); let runners_dir = root.join("runners"); let registry_dir = root.join("registry"); @@ -334,7 +375,6 @@ impl EmissionTestContext { std::fs::create_dir_all(®istry_dir).unwrap(); std::fs::write(registry_dir.join("all.json"), "{}").unwrap(); - let fees = emission_fees(); let shared_data = sync::DArc::new(rt::SharedData { allow_two_workers: true, run_mode: rt::RunMode::Leader, @@ -580,6 +620,126 @@ fn trap_message(error: generated::types::Error) -> String { trap.to_string() } +#[tokio::test] +async fn internal_exhaustion_never_spills_to_wildcard() { + for emission in [ + MessageEmission::InternalAllocation, + MessageEmission::DeployAllocation, + ] { + for budget in [U256::zero(), U256::one()] { + let mut test = EmissionTestContext::new(u32::MAX, 100); + let mut exact = internal_message_allocation(); + exact.recipient = Some(calldata::Address::zero()); + exact.call_key = Some(genvm_modules_interfaces::CallKey(match emission { + MessageEmission::InternalAllocation => abi::CallKey::UNNAMED.0, + _ => abi::CallKey::DEPLOY.0, + })); + exact.budget = Some(budget); + test.context + .data + .accumulator + .message_fee_allocation + .push(exact); + test.context + .data + .accumulator + .message_fee_allocation_consumed + .push(U256::zero()); + if !budget.is_zero() { + test.emit_message(emission).await.unwrap(); + } + let count = test.context.data.accumulator.emissions.len(); + let error = trap_message(test.emit_message(emission).await.unwrap_err()); + assert!( + error.contains("out_of message_fee allocation_budget # internal"), + "{error}" + ); + assert_eq!(test.context.data.accumulator.emissions.len(), count); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed[1], + U256::zero() + ); + test.shutdown().await; + } + } +} + +#[tokio::test] +async fn uncapped_allocation_still_obeys_pool_budget() { + let mut test = EmissionTestContext::new(u32::MAX, 2); + test.context.data.accumulator.message_fee_allocation[1].budget = None; + test.emit_message(MessageEmission::InternalAllocation) + .await + .unwrap(); + let error = trap_message( + test.emit_message(MessageEmission::InternalAllocation) + .await + .unwrap_err(), + ); + assert!( + error.contains("out_of message_fee total # internal"), + "{error}" + ); + assert_eq!(test.context.data.accumulator.emissions.len(), 1); + test.shutdown().await; +} + +#[tokio::test] +async fn external_exhaustion_spills_but_absence_is_unallocated() { + let mut test = EmissionTestContext::new(u32::MAX, 100); + let mut exact = external_message_allocation(); + exact.recipient = Some(calldata::Address::zero()); + exact.call_key = Some(genvm_modules_interfaces::CallKey([0; 32])); + exact.budget = Some(U256::zero()); + test.context + .data + .accumulator + .message_fee_allocation + .push(exact); + test.context + .data + .accumulator + .message_fee_allocation_consumed + .push(U256::zero()); + test.emit_message(MessageEmission::External).await.unwrap(); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed[0], + U256::one() + ); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed[2], + U256::zero() + ); + test.context.data.accumulator.message_fee_allocation[0].budget = Some(U256::one()); + let error = trap_message( + test.emit_message(MessageEmission::External) + .await + .unwrap_err(), + ); + assert!( + error.contains("out_of message_fee allocation_budget # external"), + "{error}" + ); + test.context.data.accumulator.message_fee_allocation.clear(); + test.context + .data + .accumulator + .message_fee_allocation_consumed + .clear(); + test.emit_message(MessageEmission::External).await.unwrap(); + assert_eq!(test.context.data.accumulator.emissions.len(), 2); + test.shutdown().await; +} + #[test] fn emission_allocation_includes_fixed_and_payload_costs() { assert_eq!( @@ -652,7 +812,7 @@ async fn messages_rejected_by_memory_are_not_appended_or_charged() { .accumulator .message_fee_allocation .iter() - .all(|node| node.budget == U256::from(100)), + .all(|node| node.budget == Some(U256::from(100))), "{} consumed its allocation budget", emission.name() ); @@ -698,7 +858,7 @@ async fn messages_rejected_by_fee_are_not_appended_and_release_memory() { .accumulator .message_fee_allocation .iter() - .all(|node| node.budget == U256::from(100)), + .all(|node| node.budget == Some(U256::from(100))), "{} consumed its allocation budget", emission.name() ); @@ -757,17 +917,15 @@ async fn unallocated_external_receipt_exhaustion_is_classified_as_receipt() { } #[tokio::test] -async fn repeated_internal_messages_preserve_canonical_subtree_and_charge_budgets() { +async fn repeated_internal_messages_preserve_opaque_subtree_and_charge_budgets() { for emission in [ MessageEmission::InternalAllocation, MessageEmission::DeployAllocation, ] { let mut test = EmissionTestContext::new(u32::MAX, 14); - let grandchild = allocation_child(U256::one(), Vec::new()); - let first_child = allocation_child(U256::from(2), vec![grandchild]); - let second_child = allocation_child(U256::from(3), Vec::new()); - test.context.data.accumulator.message_fee_allocation[1].children = - vec![first_child, second_child]; + let subtree = bytes::Bytes::from_static(b"opaque subtree and proof"); + test.context.data.accumulator.message_fee_allocation[1].children_budget = U256::from(5); + test.context.data.accumulator.message_fee_allocation[1].subtree = subtree.clone(); test.emit_message(emission).await.unwrap(); test.emit_message(emission).await.unwrap(); @@ -790,9 +948,10 @@ async fn repeated_internal_messages_preserve_canonical_subtree_and_charge_budget assert_eq!(first.0, U256::from(6), "{}", emission.name()); assert_eq!(second.0, U256::from(6), "{}", emission.name()); assert_eq!(first.1, second.1, "{} subtree changed", emission.name()); + assert_eq!(first.1, subtree, "{} subtree rewritten", emission.name()); assert_eq!( test.context.data.accumulator.message_fee_allocation[1].budget, - U256::from(100), + Some(U256::from(100)), "{}", emission.name() ); @@ -825,13 +984,156 @@ async fn repeated_internal_messages_preserve_canonical_subtree_and_charge_budget } } +#[tokio::test] +async fn allocation_zero_price_caps_reject_calls_and_deploys_without_charging() { + for emission in [ + MessageEmission::InternalAllocation, + MessageEmission::DeployAllocation, + ] { + for cap in 0..3 { + let mut test = EmissionTestContext::new(u32::MAX, 100); + let mut exact = internal_message_allocation(); + exact.recipient = Some(calldata::Address::zero()); + let genvm_modules_interfaces::fees::MessageAllocationNodeParams::Internal(params) = + &mut exact.fee_params + else { + unreachable!() + }; + let params = Arc::make_mut(params); + match cap { + 0 => params.max_price_gen_per_time_unit = U256::zero(), + 1 => params.storage_fee_max_gas_price = U256::zero(), + _ => params.receipt_fee_max_gas_price = U256::zero(), + } + test.context + .data + .accumulator + .message_fee_allocation + .push(exact); + test.context + .data + .accumulator + .message_fee_allocation_consumed + .push(U256::zero()); + let memory_before = test.context.limiter.get_remaining_memory(); + + let error = test.emit_message(emission).await.unwrap_err(); + + assert_eq!( + errno(error), + generated::types::Errno::Inval, + "{} cap {cap}", + emission.name() + ); + assert!( + test.context.data.accumulator.emissions.is_empty(), + "{} emitted", + emission.name() + ); + assert_eq!(test.context.limiter.get_remaining_memory(), memory_before); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed, + vec![U256::zero(); 3] + ); + let consumed = test + .context + .data + .supervisor + .shared_data + .data_fees_limit + .consumed() + .await; + assert_eq!(consumed.message_fee, U256::zero()); + assert_eq!(consumed.message_receipt, U256::zero()); + test.shutdown().await; + } + } +} + +#[tokio::test] +async fn opaque_subtree_length_is_charged_before_emission() { + for emission in [ + MessageEmission::InternalAllocation, + MessageEmission::DeployAllocation, + ] { + for length in [0, 1, 31, 32, 33, 512] { + for remaining in [length, length + 1] { + let mut fees = emission_fees(); + fees.message_receipt.delta_expr = "\\attrs = attrs.subtreeLength".to_owned(); + let mut test = EmissionTestContext::with_fees(u32::MAX, remaining, fees); + let subtree = bytes::Bytes::from(vec![0xab; length as usize]); + test.context.data.accumulator.message_fee_allocation[1].subtree = subtree.clone(); + let result = test.emit_message(emission).await; + if remaining == length { + let error = trap_message(result.unwrap_err()); + assert!( + error.contains("out_of message_fee total # internal"), + "{error}" + ); + assert!(test.context.data.accumulator.emissions.is_empty()); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed[1], + U256::zero() + ); + } else { + result.unwrap(); + let emitted = match &test.context.data.accumulator.emissions[0] { + domain::ExecutionEmission::InternalMessage { + subtree, + receipt_fee, + .. + } + | domain::ExecutionEmission::InternalDeployMessage { + subtree, + receipt_fee, + .. + } => (subtree, receipt_fee), + other => panic!("unexpected emission: {other:?}"), + }; + assert_eq!(emitted.0, &subtree); + assert_eq!(*emitted.1, U256::from(length)); + } + test.shutdown().await; + } + } + } +} + +#[tokio::test] +async fn children_budget_must_fit_the_matched_allocation() { + for emission in [ + MessageEmission::InternalAllocation, + MessageEmission::DeployAllocation, + ] { + let mut test = EmissionTestContext::new(u32::MAX, 1000); + test.context.data.accumulator.message_fee_allocation[1].children_budget = U256::from(100); + let error = trap_message(test.emit_message(emission).await.unwrap_err()); + assert!( + error.contains("out_of message_fee allocation_budget # internal"), + "{error}" + ); + assert!(test.context.data.accumulator.emissions.is_empty()); + assert_eq!( + test.context + .data + .accumulator + .message_fee_allocation_consumed[1], + U256::zero() + ); + test.shutdown().await; + } +} + #[tokio::test] async fn child_budget_fee_failure_is_atomic() { let mut test = EmissionTestContext::new(u32::MAX, 6); - test.context.data.accumulator.message_fee_allocation[1].children = vec![ - allocation_child(U256::from(2), Vec::new()), - allocation_child(U256::from(3), Vec::new()), - ]; + test.context.data.accumulator.message_fee_allocation[1].children_budget = U256::from(5); let memory_before = test.context.limiter.get_remaining_memory(); let error = test @@ -846,7 +1148,7 @@ async fn child_budget_fee_failure_is_atomic() { assert!(test.context.data.accumulator.emissions.is_empty()); assert_eq!( test.context.data.accumulator.message_fee_allocation[1].budget, - U256::from(100) + Some(U256::from(100)) ); assert_eq!( test.context @@ -873,8 +1175,7 @@ async fn child_budget_fee_failure_is_atomic() { #[tokio::test] async fn child_budget_overflow_is_internal_and_has_no_effect() { let mut test = EmissionTestContext::new(u32::MAX, 1); - test.context.data.accumulator.message_fee_allocation[1].children = - vec![allocation_child(U256::MAX, Vec::new())]; + test.context.data.accumulator.message_fee_allocation[1].children_budget = U256::MAX; let memory_before = test.context.limiter.get_remaining_memory(); let error = test @@ -889,7 +1190,7 @@ async fn child_budget_overflow_is_internal_and_has_no_effect() { assert!(test.context.data.accumulator.emissions.is_empty()); assert_eq!( test.context.data.accumulator.message_fee_allocation[1].budget, - U256::from(100) + Some(U256::from(100)) ); assert_eq!( test.context diff --git a/executor/tests/message_fee_time_units.rs b/executor/tests/message_fee_time_units.rs index 5eb23e0b..0482f467 100644 --- a/executor/tests/message_fee_time_units.rs +++ b/executor/tests/message_fee_time_units.rs @@ -112,5 +112,35 @@ fn primary_fee_is_not_multiplied_by_appeal_lifecycle() { let fee = fees.calculate_message_fee_internal(¶ms).unwrap(); - assert_eq!(fee.reported_fee(), U256::from(47_837)); + assert_eq!(fee.reported_fee(), U256::from(68_087)); +} + +#[test] +fn appeal_profit_prices_each_bond_before_rounding_and_is_not_taxed() { + let mut gas_data = gas_data(1, u64::MAX, 1, u64::MAX); + gas_data.insert("overlaySplitBps".to_owned(), "1500".to_owned()); + let fees = DataLimit::new(bucket_totals(), default_fees(), gas_data).unwrap(); + let mut params = fee_params(2, 1); + params.rotations = vec![U256::zero(); 3]; + params.max_price_gen_per_time_unit = U256::from(3); + + // Work = (7 + 9 + 13 + 15 + 25) * 3 = 207; overlay = floor(207 * 1500 / 8500). + // Bonds 39 and 75 reserve profits 58 and 112; execution backing is 5. + assert_eq!( + fees.calculate_message_fee_internal(¶ms) + .unwrap() + .reported_fee(), + U256::from(418) + ); +} + +#[test] +fn no_appeals_reserve_no_appeal_profit() { + let fees = DataLimit::new(bucket_totals(), default_fees(), gas_data(1, 100, 1, 100)).unwrap(); + assert_eq!( + fees.calculate_message_fee_internal(&fee_params(2, 1)) + .unwrap() + .reported_fee(), + U256::from(8) + ); } diff --git a/tests/integration/balance/balance/balance.0_0.hash b/tests/integration/balance/balance/balance.0_0.hash index 3ea5510c..03543a5e 100644 --- a/tests/integration/balance/balance/balance.0_0.hash +++ b/tests/integration/balance/balance/balance.0_0.hash @@ -1 +1 @@ -ZAezXm4BZemK1QTRf90Lks45sC1qbhNLPRu5OHg8OsM= +JB0Oezep4QthkURSOtzNGrNrvFcSn9rplyBwMLTlshE= diff --git a/tests/integration/balance/balance/balance.0_0.stdout b/tests/integration/balance/balance/balance.0_0.stdout index 122131f6..c36c55f8 100644 --- a/tests/integration/balance/balance/balance.0_0.stdout +++ b/tests/integration/balance/balance/balance.0_0.stdout @@ -7,4 +7,4 @@ main At(self) 10 nested self 10 nested At(self) 10 executed with `Return(null)` -{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":5} +{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":5} diff --git a/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.hash b/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.hash index d759b0da..11c933c1 100644 --- a/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.hash +++ b/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.hash @@ -1 +1 @@ -foLX2scBK6Cv0wXIj3CSa6PljUG7Rp9esJyy3bMuWdc= +om9ZwsG6LO2qGDr1CzrEZ4zxfPbOTqt3etru5kiNWps= diff --git a/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.stdout b/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.stdout index d3ebd911..5eeb2301 100644 --- a/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.stdout +++ b/tests/integration/balance/sandbox_overspend/sandbox_overspend.0.stdout @@ -3,4 +3,4 @@ sandbox transfer failed: 7: insufficient_balance sandbox result=Return(calldata=40) balance final=40 executed with `Return(null)` -{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":60} +{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":60} diff --git a/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.hash b/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.hash index b1679e0f..7aecabac 100644 --- a/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.hash +++ b/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.hash @@ -1 +1 @@ -SRu4v8P3dJqR1ebTnJj1jtCEe8s470ikid37hwdSTNA= +02uyKWwSjte3j/u3l7ze7PAwLl9h+0rfQ8vwM3eYZQI= diff --git a/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.stdout b/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.stdout index 0c5ed332..b23a7fd0 100644 --- a/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.stdout +++ b/tests/integration/balance/sandbox_overspend_2/sandbox_overspend_2.0.stdout @@ -3,4 +3,4 @@ sandbox result=Return(calldata=40) balance after sandbox=40 transfer failed with error: 7: insufficient_balance balance final=40 executed with `Return(null)` -{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":60} +{"address":addr#0200000000000000000000000000000000000000,"call_key":b#0000000000000000000000000000000000000000000000000000000000000000,"calldata":{},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":60} diff --git a/tests/integration/message/deploy/deploy.0.hash b/tests/integration/message/deploy/deploy.0.hash index a551a114..d29efe08 100644 --- a/tests/integration/message/deploy/deploy.0.hash +++ b/tests/integration/message/deploy/deploy.0.hash @@ -1 +1 @@ -JF0EOk2xP9rvdFHiEyRn5+ghRYkeBgKiCH+dquOE/Ds= +BOj7XyVTFi2B/pS70PGkr56OqffrRmoJjEn6NngWW2E= diff --git a/tests/integration/message/deploy/deploy.0.stdout b/tests/integration/message/deploy/deploy.0.stdout index d823fca2..3300d1ce 100644 --- a/tests/integration/message/deploy/deploy.0.stdout +++ b/tests/integration/message/deploy/deploy.0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"calldata":{},"code":b#6e6f74207265616c6c79206120636f6e7472616374,"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":2049,"salt_nonce":0,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalDeployMessage","use_balance":false,"value":0} +{"calldata":{},"code":b#6e6f74207265616c6c79206120636f6e7472616374,"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1217,"salt_nonce":0,"subtree":b#,"type":"InternalDeployMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/deploy_salt/deploy_salt.0.hash b/tests/integration/message/deploy_salt/deploy_salt.0.hash index ed7749ca..168982a3 100644 --- a/tests/integration/message/deploy_salt/deploy_salt.0.hash +++ b/tests/integration/message/deploy_salt/deploy_salt.0.hash @@ -1 +1 @@ -+Y1OyYxWT8EE+6UfttirvzhlxaHH/WQzvY9mjSaGF0U= +E90VS+xq/RN7xpr6USgvdzPTb//P8sebB1W9lNv9lvQ= diff --git a/tests/integration/message/deploy_salt/deploy_salt.0.stdout b/tests/integration/message/deploy_salt/deploy_salt.0.stdout index 86837ea8..73c4e3dc 100644 --- a/tests/integration/message/deploy_salt/deploy_salt.0.stdout +++ b/tests/integration/message/deploy_salt/deploy_salt.0.stdout @@ -1,3 +1,3 @@ 0xf539Cb83f077Cd01BDd1a4E002866dCC0D15D633 executed with `Return(null)` -{"calldata":{},"code":b#6e6f74207265616c6c79206120636f6e7472616374,"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":2049,"salt_nonce":1,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalDeployMessage","use_balance":false,"value":0} +{"calldata":{},"code":b#6e6f74207265616c6c79206120636f6e7472616374,"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1217,"salt_nonce":1,"subtree":b#,"type":"InternalDeployMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/internal_below_min_timeunits/internal_below_min_timeunits.jsonnet b/tests/integration/message/internal_below_min_timeunits/internal_below_min_timeunits.jsonnet index 8ca7a96a..f9aade18 100644 --- a/tests/integration/message/internal_below_min_timeunits/internal_below_min_timeunits.jsonnet +++ b/tests/integration/message/internal_below_min_timeunits/internal_below_min_timeunits.jsonnet @@ -41,7 +41,8 @@ local alloc = { receipt_fee_max_gas_price: 20, }, }, - children: [], + children_budget: 0, + subtree: [], }; {tags: util.features([['message', 'send'], ['fees']], 'stable') + ['python'], diff --git a/tests/integration/message/message_count_cap/message_count_cap.0.hash b/tests/integration/message/message_count_cap/message_count_cap.0.hash index ed5faadd..a383538a 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.0.hash +++ b/tests/integration/message/message_count_cap/message_count_cap.0.hash @@ -1 +1 @@ -MVSfi8henyc4QSkJ48ZD2T+vdPL2Hb7YbixoUIZlHEw= +3Vh9/mJaiAF31E208xGAiEELYPYg/brdniOHufg8+Ew= diff --git a/tests/integration/message/message_count_cap/message_count_cap.0.stdout b/tests/integration/message/message_count_cap/message_count_cap.0.stdout index 612704ac..69c8906a 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.0.stdout +++ b/tests/integration/message/message_count_cap/message_count_cap.0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/message_count_cap/message_count_cap.2.hash b/tests/integration/message/message_count_cap/message_count_cap.2.hash index a7f5fb81..5b3cfeba 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.2.hash +++ b/tests/integration/message/message_count_cap/message_count_cap.2.hash @@ -1 +1 @@ -v32eSYJTw6qnDDpQ5uofmIe86rMtxk5skTpUhKzhGbo= +6ry9qm8Ix91BuIgwjLVN+i11PoWEcCnQB1EXSI0wT1s= diff --git a/tests/integration/message/message_count_cap/message_count_cap.2.stdout b/tests/integration/message/message_count_cap/message_count_cap.2.stdout index 612704ac..69c8906a 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.2.stdout +++ b/tests/integration/message/message_count_cap/message_count_cap.2.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/message_count_cap/message_count_cap.3.hash b/tests/integration/message/message_count_cap/message_count_cap.3.hash index 4b73192b..679f2125 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.3.hash +++ b/tests/integration/message/message_count_cap/message_count_cap.3.hash @@ -1 +1 @@ -pdlmgoV9cEvFY692LPvCyMTWyn4krbsJukHBUnA0lOQ= +avr4cHakpGX9ecKiU+X8mT3htXgk850LxPLsnx+qR6U= diff --git a/tests/integration/message/message_count_cap/message_count_cap.4.hash b/tests/integration/message/message_count_cap/message_count_cap.4.hash index fad59e40..0a9c27a7 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.4.hash +++ b/tests/integration/message/message_count_cap/message_count_cap.4.hash @@ -1 +1 @@ -f4hf/t+HeQjODr+DUc1D9855Dp45zgRkT3G4hMpQpQU= +fVnNaBcVFyegjBROF457gNxlOSyJb/zxDbuQZt+2Ais= diff --git a/tests/integration/message/message_count_cap/message_count_cap.4.stdout b/tests/integration/message/message_count_cap/message_count_cap.4.stdout index 612704ac..69c8906a 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.4.stdout +++ b/tests/integration/message/message_count_cap/message_count_cap.4.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/message_count_cap/message_count_cap.5.hash b/tests/integration/message/message_count_cap/message_count_cap.5.hash index 4ca4147b..55ba9132 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.5.hash +++ b/tests/integration/message/message_count_cap/message_count_cap.5.hash @@ -1 +1 @@ -SWXrk1qR6hy/eMkGSwFmay0B2UYnLfYUZZ691c7m3Wg= +lzOMq7BjovYc5aA7MWrGfFtM7AvQYL4HUvm2/cIMKKk= diff --git a/tests/integration/message/message_count_cap/message_count_cap.jsonnet b/tests/integration/message/message_count_cap/message_count_cap.jsonnet index 926d7bb9..b7f196e3 100644 --- a/tests/integration/message/message_count_cap/message_count_cap.jsonnet +++ b/tests/integration/message/message_count_cap/message_count_cap.jsonnet @@ -6,11 +6,11 @@ local base = simpleDeploy.run('${jsonnetDir}/../send_message/send_message.py'); entry: util.addPaths([ base {bucket_totals: {submitted_messages_count: 1}}, base {bucket_totals: {submitted_messages_count: 0}}, - // 64-byte array frame + one 1888-byte conservatively encoded message - base {bucket_totals: {submitted_messages: 1952}}, - base {bucket_totals: {submitted_messages: 1951}}, - // 1095 startup + 12 storage + 1953 message receipt gas - base {bucket_totals: {execution_data_gas: 3060}}, - base {bucket_totals: {execution_data_gas: 3059}}, + // 64-byte array frame + one 1056-byte conservatively encoded message + base {bucket_totals: {submitted_messages: 1120}}, + base {bucket_totals: {submitted_messages: 1119}}, + // 1095 startup + 12 storage + 1121 message receipt gas + base {bucket_totals: {execution_data_gas: 2228}}, + base {bucket_totals: {execution_data_gas: 2227}}, ]), } diff --git a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.hash b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.hash index dae22c21..3c007349 100644 --- a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.hash +++ b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.hash @@ -1 +1 @@ -270Hiaoh4iv0vtYR5aS4pPEmGg2Cg+lbfjHdpn/Na74= +zFjD8qr23ZgJ8k18CpqAP7E8FtJolCDBRFJbKrzz4I0= diff --git a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.stdout b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.stdout index 9aa08417..f7fdb6fb 100644 --- a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.stdout +++ b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo"},"fee_params":{"execution_budget_per_round":1,"leader_timeunits_allocation":1,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":1,"rotations":[0],"storage_fee_max_gas_price":1,"validator_timeunits_allocation":1},"message_fee":75,"on":"finalized","receipt_fee":3617,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000054000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo"},"fee_params":{"execution_budget_per_round":1,"leader_timeunits_allocation":1,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":1,"rotations":[0,0],"storage_fee_max_gas_price":1,"validator_timeunits_allocation":1},"message_fee":160,"on":"finalized","receipt_fee":1057,"subtree":b#00ff010203,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.jsonnet b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.jsonnet index 378d97d2..a4918333 100644 --- a/tests/integration/message/nested_allocation_budget/nested_allocation_budget.jsonnet +++ b/tests/integration/message/nested_allocation_budget/nested_allocation_budget.jsonnet @@ -24,7 +24,7 @@ local gasData = { local params = { execution_budget_per_round: 1, - rotations: [0], + rotations: [0, 0], leader_timeunits_allocation: 1, validator_timeunits_allocation: 1, max_price_gen_per_time_unit: 2, @@ -32,25 +32,21 @@ local params = { receipt_fee_max_gas_price: 1, }; -local child(budget, children=[], recipient=null) = { - budget: budget, - recipient: recipient, +local alloc = { + budget: 200, + recipient: null, call_key: null, on: 'finalized', fee_params: {Internal: params}, - children: children, + children_budget: 60, + subtree: [0, 255, 1, 2, 3], }; -local alloc = child(100, [ - child(30, [child(13)]), - child(30, [], 'AwAAAAAAAAAAAAAAAAAAAAAAAAA='), -]); - { tags: util.features([['message', 'send'], ['fees']], 'stable') + ['python'], entry: util.addPaths([ simple_deploy.run('${jsonnetDir}/${fileBaseName}.py') { - bucket_totals: {message_fee: 100}, + bucket_totals: {message_fee: 200}, gas_data: gasData, message_fee_allocation: [alloc], }, diff --git a/tests/integration/message/send_message/send_message.0.hash b/tests/integration/message/send_message/send_message.0.hash index 28ecc528..59c6c936 100644 --- a/tests/integration/message/send_message/send_message.0.hash +++ b/tests/integration/message/send_message/send_message.0.hash @@ -1 +1 @@ -SBQLycD0f3CYK2e8XLFJr6vMb4pIulJDDxBX6ryT4HI= +UQb2gkxE347cURyeM7Ncufg1kW0PmCK6LdnLAaBRF4w= diff --git a/tests/integration/message/send_message/send_message.0.stdout b/tests/integration/message/send_message/send_message.0.stdout index 612704ac..69c8906a 100644 --- a/tests/integration/message/send_message/send_message.0.stdout +++ b/tests/integration/message/send_message/send_message.0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":34836,"on":"finalized","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":41586,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/send_message_on/send_message_on.0_0.hash b/tests/integration/message/send_message_on/send_message_on.0_0.hash index 73f21d12..8d3e7878 100644 --- a/tests/integration/message/send_message_on/send_message_on.0_0.hash +++ b/tests/integration/message/send_message_on/send_message_on.0_0.hash @@ -1 +1 @@ -RZ5fKPUOr8Y06NwTt/C5dB8wpxN+6snQLdPgTuvPU9U= +BG+CviBBbNBgbVzhKnkjhFVlh6WO2CJ6tcMC2M2HnF8= diff --git a/tests/integration/message/send_message_on/send_message_on.0_0.stdout b/tests/integration/message/send_message_on/send_message_on.0_0.stdout index 9950a323..de80066a 100644 --- a/tests/integration/message/send_message_on/send_message_on.0_0.stdout +++ b/tests/integration/message/send_message_on/send_message_on.0_0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":1606938044258990275541962092341162602522202993782792835301376,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":1606938044258990275541962092341162602522202993782792835301376,"validator_timeunits_allocation":5},"message_fee":34836,"on":"decided","receipt_fee":1953,"subtree":b#00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004,"type":"InternalMessage","use_balance":false,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":1,"receipt_fee_max_gas_price":1606938044258990275541962092341162602522202993782792835301376,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":1606938044258990275541962092341162602522202993782792835301376,"validator_timeunits_allocation":5},"message_fee":41586,"on":"decided","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":false,"value":0} diff --git a/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.hash b/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.hash index d8b05773..3a6d12cc 100644 --- a/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.hash +++ b/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.hash @@ -1 +1 @@ -t3b+pneHQ09AZ7u0IKWidOvlkE/b4Q3Hr9722TRQ4lI= +zn3+m8sYBCqoNT7la/O+Nnvdr8Gn3xnmBzURvphlUmA= diff --git a/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.stdout b/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.stdout index d527a2c5..c7dfc40f 100644 --- a/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.stdout +++ b/tests/integration/message/use_balance_no_alloc/use_balance_no_alloc.0_0_0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":39976,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":53476,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} diff --git a/tests/integration/message/use_balance_ok/use_balance_ok.0_0.hash b/tests/integration/message/use_balance_ok/use_balance_ok.0_0.hash index 3aea7659..4bb6f2db 100644 --- a/tests/integration/message/use_balance_ok/use_balance_ok.0_0.hash +++ b/tests/integration/message/use_balance_ok/use_balance_ok.0_0.hash @@ -1 +1 @@ -qnwQMAi963MLUW/9jJb2C4pD+maY8JRmJT+G5XXUqC0= +v+/q0enIeQ2YjL2n06ttkx6AvXFy1G1aH8OD3/svVL8= diff --git a/tests/integration/message/use_balance_ok/use_balance_ok.0_0.stdout b/tests/integration/message/use_balance_ok/use_balance_ok.0_0.stdout index d527a2c5..c7dfc40f 100644 --- a/tests/integration/message/use_balance_ok/use_balance_ok.0_0.stdout +++ b/tests/integration/message/use_balance_ok/use_balance_ok.0_0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":39976,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":53476,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} diff --git a/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.hash b/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.hash index 4e680766..63a5a417 100644 --- a/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.hash +++ b/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.hash @@ -1 +1 @@ -uDyvdGOnRRyMbapeblD/5xdBX4VarmqsIw5glylIdyg= +1bSpW7zr54WnkVX6maOUOWxMr7aMjfunEslACwkXUMY= diff --git a/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.stdout b/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.stdout index d527a2c5..c7dfc40f 100644 --- a/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.stdout +++ b/tests/integration/message/use_balance_on_deploy/use_balance_on_deploy.0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":39976,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":53476,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} diff --git a/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.hash b/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.hash index cf1ed967..fa88911f 100644 --- a/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.hash +++ b/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.hash @@ -1 +1 @@ -nMC6ZeKq5eUHNk/ehJyeDlRXlM/Q7A5qowbfzHsaLw8= +4mGuHGPZtkSgBLtEizZjl6WIooJgNsf6iinL/NXQ4TI= diff --git a/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.stdout b/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.stdout index 033ffe17..e7054921 100644 --- a/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.stdout +++ b/tests/integration/message/use_balance_sandbox/use_balance_sandbox.0_0.stdout @@ -1,3 +1,3 @@ sandbox: emitted executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":39976,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":53476,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} diff --git a/tests/integration/message/use_balance_scaled/use_balance_scaled.0.hash b/tests/integration/message/use_balance_scaled/use_balance_scaled.0.hash index ba90941e..0a68e692 100644 --- a/tests/integration/message/use_balance_scaled/use_balance_scaled.0.hash +++ b/tests/integration/message/use_balance_scaled/use_balance_scaled.0.hash @@ -1 +1 @@ -7BoKuiWv2Q6jpQ5oy52G08nSMLLslruuBLAuWPc9KqM= +zA3XX1UxUJiJCIx/F5K9Onxx/zSX13VVFm6eLEps3WY= diff --git a/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.hash b/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.hash index 1aaf9c26..5eb382e6 100644 --- a/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.hash +++ b/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.hash @@ -1 +1 @@ -iOpOZpyUPClRgf5Yz21aoIuDsh81PvVhdHNuJ1C9U/c= +cMf96TlPl5fPcvzq5Ik3iKwM+EAPd9Y3ZDA7Uf6ASPs= diff --git a/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.stdout b/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.stdout index d8cd776d..1f4975a0 100644 --- a/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.stdout +++ b/tests/integration/message/use_balance_scaled/use_balance_scaled.0_0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":3,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":47837,"on":"decided","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":1024,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":3,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":68087,"on":"decided","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} diff --git a/tests/integration/message/use_balance_scaled/use_balance_scaled.py b/tests/integration/message/use_balance_scaled/use_balance_scaled.py index 7e904574..bacbc208 100644 --- a/tests/integration/message/use_balance_scaled/use_balance_scaled.py +++ b/tests/integration/message/use_balance_scaled/use_balance_scaled.py @@ -4,11 +4,9 @@ # Proves the balance-funded floor scales with the GUEST cap and grosses up only # the time-unit pool. With timeUnitPool=3*5140, overlay=floor(15420*1500/8500), -# and executionTerm=1024*29: -# primary = 15420 + 2721 + 29696 = 47837 -# The per-message fee is 47837 for both decided and finalized emissions -# The jsonnet sets node.genPerTimeUnit=7; had the balance path used it the fee -# would be 7*5140 + floor(35980*1500/8500) + 29696 = 72025 +# executionTerm=1024*29 and appealProfitReserve=20250: +# primary = 15420 + 2721 + 29696 + 20250 = 68087 +# The jsonnet sets node.genPerTimeUnit=7; funding uses the guest cap of 3 _PARAMS = gl.chain.InternalMessageParams( leader_time_units_allocation=5, validator_time_units_allocation=5, diff --git a/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.hash b/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.hash index 840a7514..e0cbd1f8 100644 --- a/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.hash +++ b/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.hash @@ -1 +1 @@ -tmQbitm/WEFeB7OLxmtPT5fzroX4En3hrHy85R6EiaU= +iGXzkVvgCFfkv5eaIVnDKNZwIkLshmngSJRc43r26rI= diff --git a/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.stdout b/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.stdout index 13a164ff..7a7d644d 100644 --- a/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.stdout +++ b/tests/integration/message/use_balance_zero_budget/use_balance_zero_budget.0_0.stdout @@ -1,2 +1,2 @@ executed with `Return(null)` -{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":0,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":10280,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} +{"address":addr#3030303030303030303030303030303030303030,"call_key":b#666f6f0000000000000000000000000000000000000000000000000000000000,"calldata":{"":"foo","args":[1,2]},"fee_params":{"execution_budget_per_round":0,"leader_timeunits_allocation":5,"max_price_gen_per_time_unit":2,"receipt_fee_max_gas_price":20,"rotations":[4,4,4,4,4],"storage_fee_max_gas_price":20,"validator_timeunits_allocation":5},"message_fee":23780,"on":"finalized","receipt_fee":1121,"subtree":b#,"type":"InternalMessage","use_balance":true,"value":0} From dc7b4671ada529fdf200e91c79e33d9e6a36eca5 Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Sun, 27 Sep 2026 22:54:01 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix(executor):=20reject=20leader=20nondet?= =?UTF-8?q?=20timeout=20as=20leader=20fault=20=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- executor/src/wasi/genlayer_sdk/run.rs | 4 ++- executor/src/wasi/genlayer_sdk/tests.rs | 33 ++++++++++++------- .../_malicious-leader.jsonnet | 3 +- .../malicious-leader/ret/ret.qhv9g7.hash | 1 + .../malicious-leader/ret/ret.qhv9g7.stdout | 3 ++ 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.hash create mode 100644 tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.stdout diff --git a/executor/src/wasi/genlayer_sdk/run.rs b/executor/src/wasi/genlayer_sdk/run.rs index be3caa26..1e3721d3 100644 --- a/executor/src/wasi/genlayer_sdk/run.rs +++ b/executor/src/wasi/genlayer_sdk/run.rs @@ -178,7 +178,9 @@ fn validate_leader_vm_error(code: &str) -> Result<(), public_abi::VmError> { return Err(malformed_leader_result()); } - if code == public_abi::VmError::malformed_entry().0.as_ref() { + if code == public_abi::VmError::malformed_entry().0.as_ref() + || code == public_abi::VmError::timeout().0.as_ref() + { return Err(malformed_leader_result()); } diff --git a/executor/src/wasi/genlayer_sdk/tests.rs b/executor/src/wasi/genlayer_sdk/tests.rs index e5c35efa..9ef1be54 100644 --- a/executor/src/wasi/genlayer_sdk/tests.rs +++ b/executor/src/wasi/genlayer_sdk/tests.rs @@ -1974,6 +1974,23 @@ fn fatal_leader_outcome_cannot_be_published() { assert!(leader_outcome_for_publication(result).is_err()); } +#[test] +fn leader_timeout_is_a_fatal_leader_fault() { + let data = leader_bytes(public_abi::ResultCode::VmError, b"timeout"); + let proposal = leader_proposal_for_validation(&data); + + assert!(matches!(proposal, LeaderProposal::Rejected(..))); + let (result, encoded) = proposal.into_result_and_encoding(); + let rt::vm::RunOk::FatalVMError(error, _) = result else { + panic!("expected a fatal leader fault, got {result:?}"); + }; + assert_eq!(error, malformed()); + assert_eq!( + encoded, + rt::vm::ContractOutcome::VMError(malformed(), None).encode() + ); +} + #[test] fn malformed_leader_outcome_is_rejected_and_charged_as_a_vm_error() { let proposal = @@ -2082,7 +2099,6 @@ fn leader_vm_error_off_trie_code_is_malformed() { #[test] fn leader_vm_error_on_trie_code_passes() { for code in [ - "timeout", "forbidden", "out_of storage", "out_of memory", @@ -2261,16 +2277,13 @@ fn leader_vm_error_derived_entry_code_is_malformed() { } #[test] -fn every_generated_trie_code_is_accepted() { +fn proposable_generated_trie_codes_are_accepted() { // Guards against codegen drift: adding a `vm_error` trie entry without // regenerating `is_valid` would silently make a legitimate leader code // unproposable. Mirrors the constructors the generator emits. // - // The leader-fault nondet-output subtree is deliberately absent: it lives - // in the derived-outcome namespace and so is never proposable (see - // `leader_vm_error_in_derived_namespace_is_remapped`). + // Derived outcomes and fatal timeouts are never proposable. let codes = [ - public_abi::VmError::timeout(), public_abi::VmError::forbidden(), public_abi::VmError::wasm_trap().val(), public_abi::VmError::wasm_trap().unreachable(), @@ -2338,13 +2351,11 @@ fn leader_own_error_is_not_self_filtered() { } #[test] -fn every_stripped_leader_error_round_trips_through_acceptance() { +fn stripped_proposable_leader_errors_round_trip_through_acceptance() { // The invariant the publish path relies on: an honest leader's published - // bytes are exactly what an honest validator accepts. Only the derived - // namespace is exempt, and the leader cannot reach it (the nondet child - // cannot spawn a nondet child of its own). + // bytes are exactly what an honest validator accepts. Derived errors and + // fatal timeouts cannot be published by an honest leader. for code in [ - "timeout", "forbidden", "wasm_trap unreachable", "out_of memory wasm_memory", diff --git a/tests/integration/nondet-consensus/malicious-leader/_malicious-leader.jsonnet b/tests/integration/nondet-consensus/malicious-leader/_malicious-leader.jsonnet index 17a1931b..fe33951b 100644 --- a/tests/integration/nondet-consensus/malicious-leader/_malicious-leader.jsonnet +++ b/tests/integration/nondet-consensus/malicious-leader/_malicious-leader.jsonnet @@ -20,11 +20,12 @@ local malformed_payloads = [ [2, 255, 254], // vm_error: code is not valid UTF-8, so it needs raw bytes ]; -// Well-formed UTF-8 that the `vm_error` trie must still reject. +// Well-formed UTF-8 that leader-result validation must still reject. local rejected_vm_error_codes = [ 'i_made_this_up', // not in the trie at all 'exit_code +7', // parameter is not spelled canonically 'out_of', // non-terminal trie node + 'timeout', // fatal timeouts cannot be published as nondet output ]; local leader_nondets = diff --git a/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.hash b/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.hash new file mode 100644 index 00000000..b480e7bb --- /dev/null +++ b/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.hash @@ -0,0 +1 @@ +Dia5fTTzFgW0Y4WmyaQFi7m5gI+xuPgNeAMvRai3ac0= diff --git a/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.stdout b/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.stdout new file mode 100644 index 00000000..1f563b48 --- /dev/null +++ b/tests/integration/nondet-consensus/malicious-leader/ret/ret.qhv9g7.stdout @@ -0,0 +1,3 @@ +{"fn":"do_return","leader_nondet":[{"kind":"vm_error","value":"timeout"}]} +executed with `VMError("leader_fault nondet_output malformed")` +nondet disagreement: 0 From 7b5633f86d8bf3edcf1b3d0538673c74e4653d1c Mon Sep 17 00:00:00 2001 From: kp2pml30 Date: Tue, 29 Sep 2026 02:13:04 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix(executor):=20validate=20startup=20outpu?= =?UTF-8?q?ts=20and=20fee=20prices=20=F0=9F=94=92=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- executor/install/config/genvm.yaml | 6 ++- executor/src/exe/run.rs | 9 ++++ executor/src/lib.rs | 25 ++++++--- executor/tests/leader_output_count.rs | 53 +++++++++++++++++++ executor/tests/message_fee_external.rs | 24 +++++++++ executor/tests/message_receipt_fees.rs | 37 +++++++++++++ .../leader_outputs.0.hash | 1 + .../leader_outputs.0.stdout | 1 + .../leader_outputs.1.hash | 1 + .../leader_outputs.1.stdout | 1 + .../leader_outputs.2.hash | 1 + .../leader_outputs.2.stdout | 1 + .../leader_outputs.3.hash | 1 + .../leader_outputs.3.stdout | 1 + .../leader_outputs.4.hash | 1 + .../leader_outputs.4.stdout | 1 + .../leader_outputs.5.hash | 1 + .../leader_outputs.5.stdout | 1 + .../leader_outputs.6.hash | 1 + .../leader_outputs.6.stdout | 1 + .../leader_outputs.7.hash | 1 + .../leader_outputs.7.stdout | 1 + .../leader_outputs.8.hash | 1 + .../leader_outputs.8.stdout | 1 + .../leader_outputs.9.hash | 1 + .../leader_outputs.9.stdout | 1 + .../leader_outputs.jsonnet | 26 +++++++++ 27 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 executor/tests/leader_output_count.rs create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.hash create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.stdout create mode 100644 tests/integration/exploit/subtract_on_start_underflow/leader_outputs.jsonnet diff --git a/executor/install/config/genvm.yaml b/executor/install/config/genvm.yaml index b90218d3..739db8b4 100644 --- a/executor/install/config/genvm.yaml +++ b/executor/install/config/genvm.yaml @@ -181,12 +181,13 @@ fees: # is conservative and avoids depending on RLP prefix lengths let codeBytes = if a.isDeploy then 32 + 32 + ceilDiv a.codeLength 32 * 32 else 0 in let abiBytes = fixedBytes + calldataBytes + feeParamsBytes + subtreeBytes + codeBytes in - [ # execution_data_gas + let validReceiptPrice = if a.isInternal then true else node.lockedReceiptGasPrice > 0 in + if validReceiptPrice then [ # execution_data_gas revealGas + abiBytes * node.receiptGasPerByte + node.gasPerChangedSlot # submitted_messages , revealBytes + abiBytes # submitted_messages_count - , 1] + , 1] else vmError "fee below_minimum" nondet_output: # Gas cost and conservative compact LeaderPublicData size buckets: [execution_data_gas, nondet_outputs] @@ -246,6 +247,7 @@ fees: else if budgetTooLow then vmError "fee below_minimum" else messageFeeFloor a.matchedFeeParams # external messages reserve gasLimit at the effective chain price + else if node.lockedReceiptGasPrice <= 0 then vmError "fee below_minimum" else a.matchedFeeParams.gasLimit * (if node.lockedReceiptGasPrice < a.matchedFeeParams.maxGasPrice then node.lockedReceiptGasPrice diff --git a/executor/src/exe/run.rs b/executor/src/exe/run.rs index 279dba6f..e11115bc 100644 --- a/executor/src/exe/run.rs +++ b/executor/src/exe/run.rs @@ -304,6 +304,15 @@ pub fn handle(args: Args, mut config: config::Config) -> Result<()> { }) }; if let Some(setup_run_ok) = setup_run_ok { + let setup_run_ok = match genvm::validate_leader_output_count( + shared_data.run_mode, + 0, + leader_nondet_results.as_ref().map_or(0, Vec::len), + &setup_run_ok, + ) { + Some(error) => genvm::rt::vm::RunOk::FatalVMError(error, None), + None => setup_run_ok, + }; let host_for = |method: genvm::host::host_fns::Methods| -> usize { let m = method as usize; if m < method_hosts.len() { diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 34ef49a7..ef1db177 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -271,19 +271,28 @@ fn extra_leader_nondet_output_error( supervisor: &rt::supervisor::Supervisor, run_ok: &rt::vm::RunOk, ) -> Option { - if matches!(run_ok, rt::vm::RunOk::FatalVMError(..)) { - return None; - } - let executed = supervisor .nondet_call_no .load(std::sync::atomic::Ordering::SeqCst); let published = supervisor .leader_nondet_results .as_ref() - .map_or(0, |v| v.len().into_int_downcast_panicking()); + .map_or(0, Vec::len); + + validate_leader_output_count(supervisor.shared_data.run_mode, executed, published, run_ok) +} + +pub fn validate_leader_output_count( + run_mode: rt::RunMode, + executed: u32, + published: usize, + run_ok: &rt::vm::RunOk, +) -> Option { + if matches!(run_ok, rt::vm::RunOk::FatalVMError(..)) { + return None; + } - if !has_extra_leader_output(supervisor.shared_data.run_mode, executed, published) { + if !has_extra_leader_output(run_mode, executed, published) { return None; } @@ -293,8 +302,8 @@ fn extra_leader_nondet_output_error( Some(rt::errors::vm_error_for_leader_extra(encoded.as_slice())) } -fn has_extra_leader_output(run_mode: rt::RunMode, executed: u32, published: u32) -> bool { - run_mode != rt::RunMode::Leader && published > executed +fn has_extra_leader_output(run_mode: rt::RunMode, executed: u32, published: usize) -> bool { + run_mode != rt::RunMode::Leader && published > executed as usize } pub async fn run_with_impl( diff --git a/executor/tests/leader_output_count.rs b/executor/tests/leader_output_count.rs new file mode 100644 index 00000000..1eff8117 --- /dev/null +++ b/executor/tests/leader_output_count.rs @@ -0,0 +1,53 @@ +use genvm::{ + public_abi::VmError, + rt::{vm::RunOk, RunMode}, + validate_leader_output_count, +}; + +#[test] +fn startup_fee_errors_with_unused_outputs_are_leader_faults() { + for mode in [RunMode::Validator, RunMode::Sync] { + for error in [ + VmError::out_of().storage(), + VmError::out_of().receipt().message().val(), + VmError::out_of().receipt().nondet_output(), + VmError::out_of().message_fee().total().val(), + VmError::out_of().receipt().event(), + ] { + let expected = genvm::rt::errors::vm_error_for_leader_extra( + genvm::rt::vm::ContractOutcome::VMError(error.clone(), None) + .encode() + .as_slice(), + ); + let outcome = RunOk::VMError(error, None); + assert_eq!( + validate_leader_output_count(mode, 0, 1, &outcome), + Some(expected) + ); + assert_eq!(validate_leader_output_count(mode, 0, 0, &outcome), None); + } + } +} + +#[test] +fn only_surplus_outputs_in_consumer_modes_are_rejected() { + let outcome = RunOk::VMError(VmError::out_of().memory().val(), None); + for mode in [RunMode::Leader, RunMode::Validator, RunMode::Sync] { + for (executed, published) in [(0, 0), (1, 0), (1, 1), (1, 2)] { + let error = validate_leader_output_count(mode, executed, published, &outcome); + assert_eq!( + error.is_some(), + mode != RunMode::Leader && published > executed as usize + ); + } + } +} + +#[test] +fn existing_fatal_errors_take_precedence_over_surplus_outputs() { + for mode in [RunMode::Validator, RunMode::Sync] { + let outcome = + RunOk::FatalVMError(VmError::leader_fault().nondet_output().malformed(), None); + assert_eq!(validate_leader_output_count(mode, 0, 1, &outcome), None); + } +} diff --git a/executor/tests/message_fee_external.rs b/executor/tests/message_fee_external.rs index 57497232..913059ba 100644 --- a/executor/tests/message_fee_external.rs +++ b/executor/tests/message_fee_external.rs @@ -74,3 +74,27 @@ fn external_fee_uses_the_guest_cap_when_it_is_lower() { fn external_fee_is_price_agnostic_when_both_agree() { assert_eq!(fee(5, 1000, 5), U256::from(5000)); } + +#[test] +fn external_reservations_reject_a_zero_locked_receipt_gas_price() { + let fees = DataLimit::new(bucket_totals(), default_fees(), gas_data(0)).unwrap(); + + for gas_limit in [0, 1000] { + let error = fees + .calculate_message_fee_external(&genlayer_sdk::abi::fees::ExternalMessageParams { + gas_limit: gas_limit.into(), + max_gas_price: U256::from(7), + }) + .unwrap_err(); + assert!( + error.to_string().contains("fee below_minimum"), + "unexpected error for gas limit {gas_limit}: {error}" + ); + } +} + +#[test] +fn external_reservations_accept_the_smallest_positive_locked_price() { + assert_eq!(fee(1, 1000, 7), U256::from(1000)); + assert_eq!(fee(1, 1000, 0), U256::zero()); +} diff --git a/executor/tests/message_receipt_fees.rs b/executor/tests/message_receipt_fees.rs index 5290087a..6d92b9a2 100644 --- a/executor/tests/message_receipt_fees.rs +++ b/executor/tests/message_receipt_fees.rs @@ -15,6 +15,7 @@ fn gas_data() -> std::collections::BTreeMap { ("fixedProposeReceiptGas", 210_000), ("gasPerChangedSlot", 1_000), ("intrinsicGas", 21_000), + ("lockedReceiptGasPrice", 1), ("receiptGasPerByte", 16), ("receiptWrapperBytes", 1_024), ] @@ -89,3 +90,39 @@ async fn reveal_cost_is_charged_with_only_the_first_message() { assert_eq!(remaining["submitted_messages"], U256::zero()); assert_eq!(remaining["submitted_messages_count"], U256::zero()); } + +#[tokio::test] +async fn external_receipts_reject_nonpositive_locked_gas_prices_without_charging() { + for price in ["0", "-1"] { + let mut gas_data = gas_data(); + gas_data.insert("lockedReceiptGasPrice".to_owned(), price.to_owned()); + let fees = DataLimit::new( + std::collections::HashMap::from([ + ("execution_data_gas".to_owned(), U256::MAX), + ("message_fee".to_owned(), U256::MAX), + ("nondet_outputs".to_owned(), U256::MAX), + ("submitted_messages".to_owned(), U256::MAX), + ("submitted_messages_count".to_owned(), U256::MAX), + ]), + default_fees(), + gas_data, + ) + .unwrap(); + let remaining = fees.remaining().await; + + for is_first_message in [true, false] { + let error = fees + .calculate_message_receipt(empty_external_message(is_first_message)) + .unwrap_err(); + assert!( + error.to_string().contains("fee below_minimum"), + "unexpected error for price {price}: {error}" + ); + } + let mut internal = empty_external_message(true); + internal.is_internal = true; + assert!(fees.calculate_message_receipt(internal).is_ok()); + assert_eq!(fees.remaining().await, remaining); + assert_eq!(fees.consumed().await.message_receipt, U256::zero()); + } +} diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.hash new file mode 100644 index 00000000..8254b52e --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.hash @@ -0,0 +1 @@ +0T8x72JnbdHVjsx14uDthn07+YYfyFoOoo+6LIYubP4= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.stdout new file mode 100644 index 00000000..a2899dc6 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.0.stdout @@ -0,0 +1 @@ +executed with `VMError("out_of receipt message")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.hash new file mode 100644 index 00000000..d205516a --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.hash @@ -0,0 +1 @@ +5BQ8XW6J5XPx/6HJyd0LIYa8/SiK6YCDTJAauicRNeg= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.stdout new file mode 100644 index 00000000..38098f21 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.1.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra yjy7js")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.hash new file mode 100644 index 00000000..d205516a --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.hash @@ -0,0 +1 @@ +5BQ8XW6J5XPx/6HJyd0LIYa8/SiK6YCDTJAauicRNeg= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.stdout new file mode 100644 index 00000000..38098f21 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.2.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra yjy7js")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.hash new file mode 100644 index 00000000..d205516a --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.hash @@ -0,0 +1 @@ +5BQ8XW6J5XPx/6HJyd0LIYa8/SiK6YCDTJAauicRNeg= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.stdout new file mode 100644 index 00000000..38098f21 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.3.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra yjy7js")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.hash new file mode 100644 index 00000000..0d798799 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.hash @@ -0,0 +1 @@ +ta5F9EeB+VXQisXwtsm8a69g3BjNGkNBhK6pBK5WrTQ= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.stdout new file mode 100644 index 00000000..709efa66 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.4.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output malformed")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.hash new file mode 100644 index 00000000..13b700f2 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.hash @@ -0,0 +1 @@ +Wz3dRWehpuJ7x/qtDaI5e+xlThD2LH9uFic3B9LC+J0= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.stdout new file mode 100644 index 00000000..6420cc04 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.5.stdout @@ -0,0 +1 @@ +executed with `VMError("out_of receipt nondet_output")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.hash new file mode 100644 index 00000000..492749cd --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.hash @@ -0,0 +1 @@ +p77KDGU4QWfChR/FW9n5RuNY67cEUHSuZClB+IcnIFQ= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.stdout new file mode 100644 index 00000000..83204e34 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.6.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra 5yfaxn")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.hash new file mode 100644 index 00000000..492749cd --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.hash @@ -0,0 +1 @@ +p77KDGU4QWfChR/FW9n5RuNY67cEUHSuZClB+IcnIFQ= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.stdout new file mode 100644 index 00000000..83204e34 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.7.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra 5yfaxn")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.hash new file mode 100644 index 00000000..492749cd --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.hash @@ -0,0 +1 @@ +p77KDGU4QWfChR/FW9n5RuNY67cEUHSuZClB+IcnIFQ= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.stdout new file mode 100644 index 00000000..83204e34 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.8.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output extra 5yfaxn")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.hash b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.hash new file mode 100644 index 00000000..64f8487b --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.hash @@ -0,0 +1 @@ +WERIdE/WFEhnKxvcLOFScT76zGL6bbCa7z/X/tMgXzI= diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.stdout b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.stdout new file mode 100644 index 00000000..709efa66 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.9.stdout @@ -0,0 +1 @@ +executed with `VMError("leader_fault nondet_output malformed")` diff --git a/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.jsonnet b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.jsonnet new file mode 100644 index 00000000..065852d5 --- /dev/null +++ b/tests/integration/exploit/subtract_on_start_underflow/leader_outputs.jsonnet @@ -0,0 +1,26 @@ +local msg = import 'templates/message.json'; +local util = import 'templates/util.jsonnet'; + +local proposals = [ + {leader_nondet: []}, + {leader_nondet: [{kind: 'return', value: null}]}, + {leader_nondet: [{kind: 'return', value: std.repeat('x', 200)}]}, + {leader_nondet: [{kind: 'raw', value: [255]}]}, + {leader_public_data_raw: [255]}, +]; + +{ + tags: util.features([['exploit', 'leader-fault'], ['fees']], 'stable') + ['python'], + entry: util.addPaths([ + { + vars: {}, + modes: 'vs', + code: '${jsonnetDir}/subtract_on_start_underflow.py', + message: msg + {is_init: true}, + calldata: '{}', + bucket_totals: buckets, + } + proposal + for buckets in [{execution_data_gas: 6}, {nondet_outputs: 63}] + for proposal in proposals + ]), +} From 16b81732e12117daf7ef0335cbd32fc7d326b16d Mon Sep 17 00:00:00 2001 From: Kanan <93033289+kriss39@users.noreply.github.com> Date: Wed, 16 Sep 2026 23:11:18 +0400 Subject: [PATCH 4/4] =?UTF-8?q?fix(executor):=20zero-fill=20the=20code=20b?= =?UTF-8?q?lob=20buffer=20instead=20of=20assume=5Finit=20=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- executor/src/rt/vm/storage.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/executor/src/rt/vm/storage.rs b/executor/src/rt/vm/storage.rs index 1b013ff9..c62cf807 100644 --- a/executor/src/rt/vm/storage.rs +++ b/executor/src/rt/vm/storage.rs @@ -618,8 +618,7 @@ impl Storage { code_slot: SlotID, code_size: u32, ) -> rt::errors::Result> { - let res = Box::new_uninit_slice(code_size.into_int_comptime()); - let mut res = unsafe { res.assume_init() }; + let mut res = vec![0u8; code_size.into_int_comptime()].into_boxed_slice(); self.read(code_slot, 4, &mut res).await?;