runtime: real gas-budget enforcement + Wasmtime fuel metering (closes #51)#62
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #51.
The v0.3 runtime tracked
gas_usedper dimension but never aborted on overflow. That caveat is gone. Two layers of metering now compose so a malicious or buggy module cannot starve a node.What landed
1. Per-dimension gas budgets
The hostcall ABI documented in
spec/abi/wasm.mdalready specifiedenv.gas_consume(dim, amount). The runtime now actually enforces it.gas_budgets: HashMap<u32, u64>onHostStateInstance::set_gas_budget(dim, units)APIenv.gas_consumehostcall: returnsErr(anyhow!("out of gas: ..."))whengas_used + amount > budget. Wasmtime converts the Err to a trap; the trap propagates as anInstance::callerror.2. Wasmtime fuel
Enabled in
Config::consume_fuel(true)so every WASM instruction tics the fuel counter. Defense against the failure mode where a malicious module spins in a pure-WASM loop and never calls back into a hostcall.Instance::set_fuel(units)to set the budgetInstance::fuel_remaining()to read what's leftDEFAULT_FUEL = 10_000_000_000applied atloadtime; existing callers see no behavior change3. Tests
7 new unit tests, all passing:
gas_consume_without_budget_runs_freely: unmetered call is uncappedgas_consume_under_budget_runs_and_records_usage: budget OK -> call succeeds, usage trackedgas_consume_over_budget_traps_with_clear_error: budget < amount -> trap with "out of gas" in message, gas_used not incrementedgas_budget_only_applies_to_set_dimension: budget on dim 1 ignored when call charges dim 0fuel_default_is_sufficient_for_counter_module: 100 increment calls fit in DEFAULT_FUELfuel_is_actually_consumed: one call decreases fuel_remainingfuel_exhaustion_traps_the_call: zero fuel -> trap4. Hand-crafted
GAS_TEST_WASMconstantThe existing counter module doesn't call
gas_consume(v0.3 codegen doesn't emit it). Testing budget enforcement requires a module that does. 73 bytes of hand-crafted WASM inlib.rstests: importsenv.gas_consume, exportsdo_workwhich callsgas_consume(0, 100)and returns 1.End-to-end demo
What this PR does NOT yet do
Each is its own future issue:
gas_consumecalls anywhere. A chain that wants per-fn gas accounting either compiles its own calls in or hooks the hostcall layer to charge based on Wasmtime fuel consumed.What unblocks next