Skip to content

GasometerState::merge public API relies on unchecked gas accounting invariants #404

@xc01

Description

@xc01

GasometerState::merge performs unchecked arithmetic when merging a child gasometer back into its parent:

pub fn merge(&mut self, other: Self, strategy: MergeStrategy) {
    match strategy {
        MergeStrategy::Commit => {
            self.used_gas -= other.gas64();
            self.refunded_gas += other.refunded_gas;
        }
        MergeStrategy::Revert => {
            self.used_gas -= other.gas64();
        }
        MergeStrategy::Discard => {}
    }
}

Both operations rely on implicit invariants:

self.used_gas >= other.gas64()
self.refunded_gas + other.refunded_gas does not overflow

This is unlikely to occur in the normal execution flow, where child gasometers are created through submeter() and the parent is pre-charged before merge().

However, merge() is public, and the project is designed to support custom integrations. A custom caller could create a child GasometerState directly, bypassing submeter(). If the parent was not pre-charged, merge(Commit) or merge(Revert) can underflow. Checked builds may panic, while default release builds may silently wrap and leave incorrect gas accounting state.

It may be better to document the expected invariant for merge(), or to guard the arithmetic with debug_assert, or use checked_sub / checked_add if invalid merge states should be reported as controlled errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions