Skip to content

Patch: cross-shard, staking, rewards, consensus and the VM - #5124

Open
GheisMohammadi wants to merge 37 commits into
devfrom
fix/mainpatch
Open

Patch: cross-shard, staking, rewards, consensus and the VM#5124
GheisMohammadi wants to merge 37 commits into
devfrom
fix/mainpatch

Conversation

@GheisMohammadi

@GheisMohammadi GheisMohammadi commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is a patch. No new features, no protocol redesign, no storage format changes. It is a set of independent corrections found during a review of the cross-shard/receipt path, the staking and delegation flow, reward distribution, EPoS and quorum, view change, and the EVM.

Severity

Severity Meaning
Critical Deterministic node halt, or value created/destroyed
High Incorrect balances, rewards or voting power
Medium Unmetered or wasted work, or state that cannot be read back
Low Hardening; no behavioural difference in practice today

Fixes

# Area Issue Fix Severity Gated
1 Consensus / view change senderKeySanityChecks verified only that a VIEWCHANGE message was self-signed, not that the key was in the committee. GetM3Bitmap aggregates every stored signature but returns a bitmap covering committee members only, so a non-member signature left the M3 aggregate unverifiable against the bitmap-derived key and no NEWVIEW could be accepted for that viewID. Reject view change senders not in decider().Participants(). Critical No
2 Slashing delegatorSlashApplyDebt divides by totalExternalStake. Delegation entries persist after a full undelegation, so a validator can carry external delegators whose snapshot stake sums to zero. Skip external apportionment when there is no external stake. Self-stake slashing is unchanged. Critical No
3 Vote power votepower.Compute divides each slot's effective stake by roster.TotalEffectiveStake, which can be zero. Compute the group share only when the total is non-zero. Critical No
4 Cross-shard receipts ValidateCXReceiptsProof indexes CXShardHashes by len(ShardIDs). The two are independent RLP lists on a gossip message. Reject mismatched lengths and a nil merkle proof. Critical No
5 Cross-shard transfers TxType is transaction-scoped but consulted on every transfer in Call and create, so nested value transfers during a SubtractionOnly transaction were debited without being credited. Only the depth-0 transfer is treated as cross-shard. Critical Yes
6 Staking / delegation VerifyAndDelegateFromMsg fetches a fresh deep copy of the wrapper per index entry. Two entries naming the same validator each consumed the same undelegating tokens, and only the last write survived. Resolve each validator through a single wrapper. Critical Yes
7 Vote power With HMYSlotCount == 0, ourPercentage stays 0 while theirPercentage only reaches externalPercent, so the "give diff to last staked voter" balancing step assigned the entire Harmony vote share to one member. Scale externalPercent to 1 when there are no Harmony slots. High Yes
8 Rewards Quo and RoundInt both round, so per-delegator shares can sum above the pool. rewardPool went negative and the remainder branch was skipped. Clamp each payout to the remaining pool. High Yes
9 Staking / undelegation In RemoveUnlockedUndelegations, an entry matching maxRateApplies was dropped from the list without being added to totalWithdraw. Pay out every entry that is removed. High Yes
10 Cross-shard transfers A cross-shard transaction with To == nil produced a CXReceipt that RLP-encodes but fails to decode, so ReadCXReceipts errored and CXMerkleProof silently skipped that shard, invalidating every proof from the same source block. Require a recipient on cross-shard transactions. High Yes
11 Cross-shard transfers CallCode and DelegateCall dispatch to precompiles without the value transfer Call performs, while still passing a contract.Value() from the calling frame, so the cross-shard precompile spent the balance held at its own address. Restrict the precompile to plain CALL via a DirectCallOnly marker. High Yes
12 Block import VerifyIncomingReceipts ran only from ValidateNewBlock. insertChain applied incoming receipts without the spent-marker, ToShardID, merkle proof or source-shard signature checks. Verify incoming receipts on the insert path. High Yes
13 Staking / rewards VerifyAndCollectRewardsFromDelegation and the redelegation loop indexed wrapper.Delegations by an offset from the node-local delegation index without checking DelegatorAddress. Require the delegation to belong to the named delegator. High Yes
14 Cross-shard receipts AddPendingReceipts admitted a proof whose validation failed with MsgNoShardStateFromDB, which is reachable for any header epoch without a stored shard state, and proposeReceiptsProof requeued it indefinitely. Bound the header epoch to current + 1 and cap requeues. High No
15 Block storage CXReceiptsProof.Copy wrote CommitSig into CommitBitmap. Block.Body() and ReadBlock both go through Copy, so persisted bodies no longer matched IncomingReceiptHash. Copy both fields correctly. High No
16 Rewards AddReward indexed curValidator.Delegations[i] by snapshot position with no length check. Bounds-check against the snapshot before distributing. High No
17 EVM Validator accounts store an RLP wrapper in the code field. Call and CallCode null it out; DelegateCall and StaticCall executed it as bytecode. Treat validator accounts as codeless in all four frames. Medium Yes
18 Staking / validator CreateValidatorFromNewMsg verified every BLS proof of possession before SanityCheck applied MaxBLSPerValidator, and this runs in TxPool.validateStakingTx before acceptance. Apply the key-count limit before signature verification. Medium No
19 Staking / validator checkDuplicateFields loaded every wrapper in the validator list even when the message supplied neither an identity nor slot keys. Skip the scan when there is nothing to compare. Medium Yes
20 Staking / delegation evm.StakeMsgs was not rolled back on frame revert, unlike CXReceipt, and was returned from ApplyTransaction even on failed execution. Snapshot and restore StakeMsgs at all five frames. Medium Yes
21 Staking / validator Finalise logged and skipped a wrapper whose SanityCheck failed, leaving stateValidators and the persisted code out of sync. Flush cached wrappers as they stand. Medium Yes
22 Rewards VerifyAndCollectRewardsFromDelegation tested emptiness with totalRewards.Int64(), which reads only the low 64 bits. Test with Sign(). Low Yes
23 Block processing MayBalanceMigration called .Root() on the result of GetBlockByHash without a nil check. Check the parent resolves first. Low No
24 Cross-shard receipts ContainsEmptyField dereferenced the receiver after the cxp == nil short-circuit. Return early on a nil receiver. Low No
25 Staking / validator append(val.SlotPubKeys[0:0], ...) aliases the caller's backing array rather than copying. Use a full slice expression. Low No
26 Node messages msgPayload[0] was read before checking the payload length. Guard the empty payload. Low No
27 Rewards if i < 0 on a uint64 loop variable is unreachable. Removed, with the range invariant documented. Low No
28 Blockchain config GetVMConfig returned &bc.vmConfig, so a caller setting a tracer would mutate the config block processing reads. Return a copy. Low No
29 Legacy sync bytes.Equal treats nil and empty as equal, so a NEWBLOCK omitting peerHash matched any peer whose hash was unset -- Low No
30 RPC SetNodeToBackupMode was exported on PublicBlockchainService, so reflection published it on public HTTP/WS -- Low No

Epochs

StrictStateValidationEpoch gates all 14 rows marked Gated: Yes.

Network Epoch
Mainnet TBD
Testnet 7645
Devnet (partner) 56874
Localnet 1
Pangaea, Stressnet 1

Testing

Every fix has a test that fails without the change and passes with it.

Packages verified: core, core/vm, core/state, core/types, consensus, consensus/quorum, consensus/votepower, core_test, node/harmony, internal/chain, internal/params, staking/types, staking/slash, staking/effective, staking/availability, hmy.

@GheisMohammadi GheisMohammadi self-assigned this Aug 18, 2026
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.

1 participant