Skip to content

feat(gas-keys): stabilize gas keys at protocol version 85#15183

Merged
darioush merged 4 commits into
masterfrom
darioush/gas-keys/stabilize
Jun 3, 2026
Merged

feat(gas-keys): stabilize gas keys at protocol version 85#15183
darioush merged 4 commits into
masterfrom
darioush/gas-keys/stabilize

Conversation

@darioush

@darioush darioush commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Gas Keys (NEP-611)

Gas keys are a new type of access key that carries a pre-funded NEAR balance and multiple independent nonce sequences. When a transaction is signed with a gas key, gas costs are deducted from the gas key's balance rather than the signer's account balance (deposits like NEAR transfers still come from the account).

Why do we need gas keys?

  • Enabling SPICE without a DoS vector. The SPICE project (Separation of Execution from Consensus) decouples transaction execution from block production: transactions are included in blocks first, and executed asynchronously later. This means chunk producers can no longer verify account balances at inclusion time. An attacker could drain an account's balance in one transaction, then flood the chain with many transactions signed by that account. All would be accepted (since execution hasn't caught up), consuming block space for free when they inevitably fail. Gas keys close this gap by requiring gas to be pre-committed in the key itself, so chunk producers can verify gas availability without access to the latest state.

  • Parallel transaction submission. Today, programmatic users who submit many transactions in parallel must manage multiple access keys (each with its own nonce sequence). A gas key supports up to 1,024 independent nonce slots via a single key, removing that complexity.

Context

Testing and QA

Checklist

@darioush darioush requested a review from a team as a code owner February 23, 2026 21:30
@darioush darioush requested review from Copilot, saketh-are and shreyan-gupta and removed request for Copilot and saketh-are February 23, 2026 21:30
@darioush darioush force-pushed the darioush/gas-keys/stabilize branch from 4aa89a4 to 0cc3dd4 Compare February 23, 2026 21:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR stabilizes the ProtocolFeature::GasKeys feature by moving it into the stable protocol feature set at protocol version 85, updating stable protocol version constants accordingly, and aligning tests/snapshots/changelog with the new stable behavior.

Changes:

  • Promote ProtocolFeature::GasKeys to stable at PV 85 and bump STABLE_PROTOCOL_VERSION to 85.
  • Remove nightly-only ignore gating from test-loop gas key tests so they run on stable.
  • Update chain snapshot hashes and add a changelog entry documenting GasKeys stabilization.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
test-loop-tests/src/tests/gas_keys.rs Removes nightly-only ignore gates so GasKeys tests run under stable builds.
core/primitives-core/src/version.rs Moves GasKeys to stable at PV 85 and bumps STABLE_PROTOCOL_VERSION to 85.
chain/chain/src/tests/simple_chain.rs Updates insta snapshot hashes to reflect the new stable protocol version/feature set.
CHANGELOG.md Documents GasKeys stabilization in the unreleased protocol changes section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CHANGELOG.md Outdated
### Protocol Changes
* The contract runtime has been upgraded to use the new Wasmtime-based runtime;
* The contract runtime now allows for bulk memory instructions in Wasm code.
* Stabilize GasKeys: transactions can now specify a gas key to pay for gas costs separately from the signer's main access key.

Copilot AI Feb 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new changelog bullet uses imperative tense ("Stabilize") while the surrounding bullets use past/present perfect ("has been upgraded", "now allows"). Consider rephrasing for consistency (e.g., "GasKeys stabilized" / "GasKeys has been stabilized") and aligning end punctuation with the other entries in this section.

Suggested change
* Stabilize GasKeys: transactions can now specify a gas key to pay for gas costs separately from the signer's main access key.
* GasKeys has been stabilized: transactions can now specify a gas key to pay for gas costs separately from the signer's main access key.

Copilot uses AI. Check for mistakes.
@codecov

codecov Bot commented Feb 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.74%. Comparing base (8539f35) to head (33d4f2b).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #15183   +/-   ##
=======================================
  Coverage   69.73%   69.74%           
=======================================
  Files         944      944           
  Lines      201033   201032    -1     
  Branches   201033   201032    -1     
=======================================
+ Hits       140193   140202    +9     
+ Misses      56133    56125    -8     
+ Partials     4707     4705    -2     
Flag Coverage Δ
pytests-nightly 1.12% <ø> (+<0.01%) ⬆️
unittests 69.48% <ø> (+0.15%) ⬆️
unittests-nightly 69.46% <ø> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@darioush darioush requested a review from pugachAG February 25, 2026 17:59
@darioush

Copy link
Copy Markdown
Contributor Author

Per our procedures for stabilization, this PR requires additional sign off.
Please take a look @pugachAG, thanks.

https://github.com/near/nearcore/blob/master/docs/practices/protocol_upgrade.md#feature-stabilization

@pugachAG pugachAG left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we stabilize the feature after NEP is fully approved

@darioush darioush marked this pull request as draft February 27, 2026 14:21
@shreyan-gupta shreyan-gupta removed their request for review May 12, 2026 21:44
@darioush darioush force-pushed the darioush/gas-keys/stabilize branch 3 times, most recently from 285123b to fa95fae Compare June 1, 2026 16:35
@darioush darioush force-pushed the darioush/gas-keys/stabilize branch from fa95fae to 55b2491 Compare June 1, 2026 16:48
@darioush darioush requested a review from Copilot June 1, 2026 16:58
@darioush

darioush commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

Why some test-loop tests switched to backwards_compatible_rs_contract

Gas keys adds three contract host functions (promise_batch_action_transfer_to_gas_key, promise_batch_action_add_gas_key_with_full_access, promise_batch_action_add_gas_key_with_function_call) that the runtime exposes only from protocol version 85. On the test side these are declared under #[cfg(feature = "latest_protocol")], so they live in rs_contract (and nightly_rs_contract), alongside the other stabilized host fns.

The catch: the always-exported call_promise dispatcher references these host fns unconditionally, so the compiled wasm emits them as host imports (unlike e.g. p256_verify, which is declared but never called and therefore dead-code-eliminated). WASM instantiation links imports eagerly — every imported host function must be provided by the host at the protocol version the contract is instantiated at. Below v85 the host does not provide the gas-key imports.

Protocol-upgrade tests that instantiate rs_contract before the upgrade (i.e. at a protocol < 85) therefore fail at link time:

Link Error: unknown or invalid import

The three tests that do this were pointed at backwards_compatible_rs_contract, which is built without latest_protocol and so carries none of the v85 imports. This is the contract the docs already prescribe for protocol-upgrade tests (resharding_v3 uses it):

  • yield_resume::test_yield_resume_across_protocol_upgrade — calls the contract at old_protocol = InstantPromiseYield - 1 = 82, before upgrading.
  • cache_warming::slow_test_cache_warming_across_vm_config_change — calls at PROTOCOL_VERSION - 1 = 84, by design (pre-upgrade cache warming).
  • deterministic_account_id::test_..._pre_fix — runs at FixDelegatedDeterministicStateInit - 1 = 84; its shared TestEnv now picks the contract by version.

Tests that only instantiate the contract at/after the upgrade (≥ 85) — e.g. those in global_contracts_distribution — were left on rs_contract, since the imports resolve fine there. This was verified per-test rather than assumed.

🤖 -- Claude on behalf of Darioush

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Comment thread test-loop-tests/src/tests/gas_keys.rs Outdated
@darioush darioush marked this pull request as ready for review June 1, 2026 17:18
@darioush darioush requested a review from pugachAG June 1, 2026 17:18
darioush added a commit that referenced this pull request Jun 1, 2026
rs_contract now imports the chain_id host fn (latest_protocol), so it no
longer links below protocol 85. Point the protocol-upgrade tests that
instantiate it pre-upgrade at backwards_compatible_rs_contract.

Byte-for-byte identical to the same fix in #15183; kept as a standalone
commit so it can be dropped if #15183 lands first.
darioush added a commit that referenced this pull request Jun 1, 2026
rs_contract now imports the chain_id host fn (latest_protocol), so it no
longer links below protocol 85. Point the protocol-upgrade tests that
instantiate it pre-upgrade at backwards_compatible_rs_contract.

Byte-for-byte identical to the same fix in #15183; kept as a standalone
commit so it can be dropped if #15183 lands first.
darioush added a commit that referenced this pull request Jun 1, 2026
rs_contract now imports the chain_id host fn (latest_protocol), so it no
longer links below protocol 85. Point the protocol-upgrade tests that
instantiate it pre-upgrade at backwards_compatible_rs_contract.

Byte-for-byte identical to the same fix in #15183; kept as a standalone
commit so it can be dropped if #15183 lands first.
darioush added a commit that referenced this pull request Jun 1, 2026
rs_contract now imports the chain_id host fn (latest_protocol), so it no
longer links below protocol 85. Point the protocol-upgrade tests that
instantiate it pre-upgrade at backwards_compatible_rs_contract.

Byte-for-byte identical to the same fix in #15183; kept as a standalone
commit so it can be dropped if #15183 lands first.
darioush added a commit that referenced this pull request Jun 1, 2026
rs_contract now imports the chain_id host fn (latest_protocol), so it no
longer links below protocol 85. Point the protocol-upgrade tests that
instantiate it pre-upgrade at backwards_compatible_rs_contract.

Byte-for-byte identical to the same fix in #15183; kept as a standalone
commit so it can be dropped if #15183 lands first.
# Conflicts:
#	core/primitives-core/src/version.rs
# Conflicts:
#	core/parameters/src/config_store.rs
#	core/parameters/src/snapshots/near_parameters__config_store__tests__149.json.snap
#	core/parameters/src/snapshots/near_parameters__config_store__tests__testnet_149.json.snap
@darioush darioush enabled auto-merge June 3, 2026 13:21
@darioush darioush added this pull request to the merge queue Jun 3, 2026
Merged via the queue into master with commit 2aa23e2 Jun 3, 2026
27 checks passed
@darioush darioush deleted the darioush/gas-keys/stabilize branch June 3, 2026 14:18
pull Bot pushed a commit to Superoldman96/nearcore that referenced this pull request Jun 3, 2026
## Summary

Implements the [NEP-638](near/NEPs#638)
`chain_id` host function, which lets a contract read the chain
identifier (e.g. `mainnet`, `testnet`) at runtime instead of hardcoding
it or passing it in as an argument. Gated behind protocol version 85.

Signature: `chain_id(register_id: u64)` — writes the chain id bytes into
the given register. Cost is `base + write_register_base +
write_register_byte * num_bytes`; no new gas parameters are introduced
(it reuses the existing register-write costs).

## Design notes

- The value is fetched lazily through the `External` trait inside the
host function (the same pattern as `validator_total_stake`), rather than
being pre-populated into `VMContext`. So there is no per-call allocation
unless a contract actually calls `chain_id`. `RuntimeExt::chain_id()`
reads it from the epoch info provider.
- Import gating is handled by `Config::chain_id_host_fn`: pre-v85 the
`env.chain_id` import is not exposed, so a contract importing it fails
to link.

## Testing

- VM logic unit test:
`runtime/near-vm-runner/src/logic/tests/context.rs::test_chain_id`.
- VM integration test:
`runtime/near-vm-runner/src/tests/chain_id_integration.rs` runs
`ext_chain_id` through every VM backend (NearVm + Wasmtime) and checks
the returned bytes.
- e2e test-loop tests: `test-loop-tests/src/tests/chain_id.rs` covers
the full transaction → receipt → outcome pipeline —
`test_chain_id_returns_chain_id` asserts the genesis chain id at v85,
and `test_chain_id_pre_activation_call_fails` confirms a contract
importing `env.chain_id` fails to link at protocol 84.
- The contract side reuses the shared `test-contract-rs` via a new
`ext_chain_id` export, gated `#[cfg(feature = "latest_protocol")]` so it
only ships in `rs_contract` (not `backwards_compatible_rs_contract`).

## Notes

- Because `rs_contract` now imports `env.chain_id`, it no longer links
below protocol 85. Three protocol-upgrade tests that instantiate it
pre-upgrade (`cache_warming`, `deterministic_account_id`,
`yield_resume`) are pointed at `backwards_compatible_rs_contract`. This
fix is byte-for-byte identical to the same change in near#15183 and is kept
as a standalone commit so it can be dropped cleanly if near#15183 lands
first.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants