CODE_COLOR: CODE_YELLOW_TESTNET
RELEASE_VERSION: 2.14.0-rc.1
PROTOCOL_UPGRADE: TRUE
DATABASE_UPGRADE: TRUE
SECURITY_UPGRADE: FALSE
Protocol Changes
- Remove gas rewards: executing a
FunctionCallno longer pays part of the burned gas back to the contract account as a reward. Theburnt_gas_rewardparameter is changed from 30% (3/10) to 0%. See HSP-027 - Bounded the combined size of a receipt's resolved promise inputs with a new
max_receipt_total_input_sizelimit of 4_194_944 bytes. Receipts which exceed the limit fail with the newTotalPromiseInputSizeExceedederror without executing their actions. Previously this data was effectively unbounded, as it is read into the state witness before the per-receipt storage proof limit starts counting. - Added an
ml_dsa_verifyhost function so contracts can verify FIPS 204 ML-DSA-65 signatures on-chain. It takes the signature (3309 bytes), an arbitrary-length message, and the raw 1952-byte public key, each passed either from memory or from a register, and returns 1 for a valid signature or 0 for an invalid one; a wrongly-sized signature or public key aborts the call withMlDsaVerifyInvalidInput. The message is verified as-is with an empty context string, so callers must not pre-hash it. It costs a fixed base cost of 540 Ggas and a dynamic per-byte cost of 11 Mgas per byte. - The per-receipt storage proof limit now applies to every action in a receipt, not just
FunctionCall. The recorded proof is checked after each action, and a receipt that goes overper_receipt_storage_proof_size_limit(4 MB) fails at that action with the newReceiptStorageProofSizeExceedederror; the actions after it do not run. Clients that decodeActionErrorKindshould expect the new variant. (#16171) - Removed the
DelegateV2meta-transaction action introduced in 2.13.0. A new transaction or receipt carrying it is now rejected at action validation with the newActionsValidationError::RemovedProtocolFeatureerror. Its inner nonce advances a gas key of the delegate sender, which the SPICE pending transaction queue does not track, because that queue reads only the outer transaction's signer, public key and nonce index. Gas keys (NEP-611) therefore can no longer sign meta transactions at all, since the olderDelegateaction already rejects a gas key signer withDelegateActionRequiresNonGasKey. Receipts created before the upgrade keep executing, so meta transactions already in flight across the upgrade boundary still complete. TheAction::DelegateV2andVersionedDelegateActionPayloadtypes stay in the codebase to hold a place for a future delegate action version. Clients that decodeActionsValidationErrorshould expect the new variant. - Early chunk producer reassignment. A chunk producer that stops producing used to keep its chunk slots until the end of the epoch, because the kickout set only applies at an epoch boundary. The protocol now tracks per-shard production stats within the epoch and stops assigning new chunks to a producer on a shard once it has missed at least 100 chunks there and its produced-to-expected ratio on that shard has fallen below 80%. The freed slot goes to a healthy producer. Stats never cross an epoch boundary, and a 1000-block grace window at the start of each epoch delays the first possible exclusion to roughly block 1000 of the epoch. A safety valve keeps the least-bad producer eligible when the rule would exclude every distinct producer on a shard, so a shard can never run out of chunk producers. The resulting assignment is precomputed per block and persisted in the
ChunkProducersdatabase column, keyed by the chunk's grandparent block, and chunk validation reads that row verbatim instead of re-sampling. Partial state witness, contract-accesses and contract-deploys network messages switch to their V2 wire variants at the same protocol version, since they now carry the grandparent anchor needed to resolve the producer. See Early chunk producer reassignment. - Rejected a
WithdrawFromGasKeyaction nested inside a delegate action, with the newActionsValidationError::WithdrawFromGasKeyNotAllowedInDelegateerror.WithdrawFromGasKeystays valid as a top-level action; only the nesting is refused. The SPICE pending transaction queue scans only a transaction's top-level actions forWithdrawFromGasKey, so a nested one drains a gas key that the queue still counts as funded. Receipts created before the upgrade keep executing, so delegate actions already in flight across the upgrade boundary still complete. Clients that decodeActionsValidationErrorshould expect the new variant. - A
FunctionCallto a contract whose compiled module fails to load now charges the contract-loading fee and aborts, instead of finalizing as a zero-gas no-op. The fee iscontract_loading_basepluscontract_loading_bytesper byte of contract code, and it is burned even though the call runs no contract code. The call failed before this change too, so the only difference is that the caller now pays for the loading work. (#15923, #16315) - Fixed a
FunctionCallto a just-distributed global contract failing when the call runs in the same chunk as the distribution receipt and the compiled contract is not in the node's contract cache. The distribution now records the code with the chunk's contract tracker, as a regularDeployContractalready does, so the call finds the code instead of failing withCodeDoesNotExist. (#16371) - Fixed two ML-DSA-65 cost-charging issues, both of which are no-ops for the classical signature schemes where the relevant quantities coincide. Gas keys now price the exec (storage) fee on the on-trie identifier length (32-byte key hash for ML-DSA-65) and the send (transmission) fee on the wire length, instead of pricing the exec fee on the wire length. Meta transactions now meter the inner
DelegateActionsignature verification compute on the receiver shard that actually runs the verification, instead of the signer shard, so it counts against the right shard's compute limit. (#16064) - Added the
sha3_256,sha3_384andsha3_512host functions so contracts can compute FIPS-202 SHA-3 digests. Each takes the input either from memory or from a register and writes the digest (32, 48 or 64 bytes respectively) into a register.sha3_256costs a base of ~5.88 Ggas plus ~21.5 Mgas per input byte;sha3_384andsha3_512cost a base of ~5.81 Ggas plus ~36.6 Mgas per input byte. (#16006, #16029) - Limited the total number of WASM locals in a contract to at most one local per two bytes of contract code. This size-dependent limit applies alongside the existing absolute limit of 1,000,000 locals per contract. #16348
- Stabilized universal accounts: a
0uaccount scheme that unifies implicit and contract-created (deterministic) account creation under one post-quantum-safe derivation. See #16096 for the full set of changes. - A
FunctionCallaction with an emptymethod_nameis now rejected at action validation with the newActionsValidationError::FunctionCallEmptyMethodNameerror, so the transaction is refused before it is admitted. Previously such a transaction was admitted and failed on-chain in the VM withMethodResolveError::MethodEmptyName, still burning gas. Clients that decodeActionsValidationErrorshould expect the new variant. (#16089) - Capped the total number of storage entries carried by the state-init actions in one receipt at 1_500, counted across the receipt and into nested delegate actions. A receipt over the limit is rejected at action validation with the new
ActionsValidationError::TotalNumberOfStateInitEntriesExceedederror. (#16408)
Non-protocol Changes
- The
ChunkProducersdatabase column is now created by every build rather than only by nightly builds, which raisesDB_VERSIONfrom 49 to 50. A node started read-write migrates its hot and cold databases in place on startup; the migration only creates the column family and rewrites no data. A read-only process cannot create a column family, so a node opened read-only against a version-49 database will refuse to start. Start the node read-write once before pointing a read-only process at the data directory. (#16301) - State sync now verifies reconstructed shard data against the next new chunk before block sync. A mismatch stops the node and preserves the data directory for diagnosis instead of attributing the failure to the chunk producer. (#15994)
- Removed the option to disable state snapshots. Nodes now always take a state snapshot every epoch, which is what lets them serve state parts to peers over decentralized state sync and is required by cloud archival. Config change: the
store.state_snapshot_configfield is gone, along with itsstate_snapshot_typevaluesEnabled/Disabledand the legacy aliasesEveryEpoch/ForReshardingOnly. Aconfig.jsonthat still containsstore.state_snapshot_configkeeps loading (the field is ignored), but a node that previously set it toDisabled(orForReshardingOnly) will now take snapshots every epoch. Offline tools such asfork-networkcontinue to run without snapshots. - Removed the long-deprecated
num_block_producer_seats_per_shard,avg_hidden_validator_seats_per_shard, andnum_chunk_only_producer_seatsfields fromEpochConfig. They were given serde defaults in 2.12 and are unused. Existing epoch-config JSON files that still contain these keys keep loading, as the keys are now ignored. (#15481) - Removed centralized (external-storage) state sync. Nodes now always sync state from peers, which has been the default for a long time. Breaking config change:
state_sync.syncno longer acceptsExternalStorage; a node whoseconfig.jsonstill sets"state_sync": {"sync": {"ExternalStorage": ... }}will fail to start. Remove thestate_sync.syncblock (peer-based sync is the default) before upgrading. The deprecated--state-sync-bucketflag and thestate-parts-dump-checktool are also removed. Dumping state to external storage (state_sync.dump) is unchanged. - Added optional pagination to
EXPERIMENTAL_view_access_key_listand theview_access_key_listquery. The request takesafter_keyandlimit, and the response returnslast_keyto fetch the following page. A new node-config knobview_access_keys_limit(default 100) bounds the number of keys returned per response; when unset (null) it falls back to the default of 100. Behavioral change: an unpaginatedview_access_key_listrequest (nolimit/after_key) now fails for any account holding more thanview_access_keys_limit(default 100) access keys; such callers must switch to paginated requests. A paginated request whoselimitexceeds the configured bound is clamped down to it rather than rejected. Operators can raise or lower the bound viaview_access_keys_limit. - Transaction-status timeouts (
tx,EXPERIMENTAL_tx_status, andsend_tx/broadcast_tx_commitwithwait_until) now carry acausein theTIMEOUT_ERROR'sinfopayload explaining how far the transaction got:NOT_OBSERVED,PENDING(with the last-known status),DOES_NOT_TRACK_SHARD, orERROR(withdebug_info). Theinfopayload is absent on responses from older nodes, so clients should treat it as optional. Previously the timeout gave no detail. - Added three Prometheus metrics for the blocks delay tracker:
near_blocks_delay_tracker_entries(gauge, labelled by map),near_blocks_delay_tracker_refused_blocks_total(labelled by reason), andnear_blocks_delay_tracker_missing_entries_total(labelled by mark). The tracker now records a block only while its height is within[head - 50, head + 500)and only up to 8 blocks per height, so/debug/api/chain_processing_statusno longer lists blocks far from the head. Itsblocks_delay_trackererror logs about a block that "was not marked received" are now a counter and a debug log, since a missing entry is expected once tracking is bounded. - Raised the minimum supported Rust version to 1.95.0. Building
nearcorefrom source now requires a Rust 1.95.0 toolchain. tools/debug-uinow builds with Vite instead of the unmaintained Create React App (react-scripts). Build output layout and the port 3000 dev server are unchanged, so deployment needs no change. Building now requires Node^20.19.0 || >=22.12.0. (#16175)- Stabilized
EXPERIMENTAL_tx_statusand renamed it totx_status.EXPERIMENTAL_tx_statuskeeps working as a deprecated alias, with the same request and response types. Metrics change: thenear_rpc_wait_until_countmetric now labelstx,tx_statusandEXPERIMENTAL_tx_statusseparately; previously bothtxandEXPERIMENTAL_tx_statuswere counted under thetx_statuslabel. (#16270) DBCol::EpochLightClientBlocksnow holds a versionedStoredLightClientBlockinstead of theLightClientBlockViewRPC type, which raisesDB_VERSIONfrom 50 to 51. A node started read-write migrates its hot database in place on startup, rewriting one row per epoch; the column holds a few thousand rows in total. The column is never garbage collected, so its rows were written by binaries going back to 2020 and appear in several borsh layouts; the migration reads each of them and rewrites it as the versioned type. A row that no layout reads stops the migration and names its epoch id rather than being guessed at. (#16394)
Protocol upgrade voting
This release upgrades the protocol version from 85 to 87.
Voting for protocol version 87 will start on Wednesday September 23rd 00:00 UTC.
To continue participating in consensus, you need to upgrade your node before this time.
If voting succeeds in one epoch, the protocol upgrade to version 87 is expected to happen 7-14 hours after the voting epoch ends.