From c195132aade4fb90be8ae464d43742176998b5e2 Mon Sep 17 00:00:00 2001 From: Weixie Cui Date: Thu, 7 May 2026 23:03:10 +0800 Subject: [PATCH] core/types/bal: enforce strict tx index ordering in validation Replace slices.IsSortedFunc checks with explicit ascending loops so duplicate consecutive indices are rejected. Add regression tests for balance, nonce, and code change lists. --- core/types/bal/bal_encoding.go | 24 ++++++------- core/types/bal/bal_test.go | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/core/types/bal/bal_encoding.go b/core/types/bal/bal_encoding.go index 03f97f3809..20c41feea0 100644 --- a/core/types/bal/bal_encoding.go +++ b/core/types/bal/bal_encoding.go @@ -180,24 +180,24 @@ func (e *AccountAccess) validate(rules params.Rules) error { } // Check the balance changes are sorted in order - if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int { - return cmp.Compare[uint32](a.TxIdx, b.TxIdx) - }) { - return errors.New("balance changes not in ascending order by tx index") + for i := 1; i < len(e.BalanceChanges); i++ { + if e.BalanceChanges[i-1].TxIdx >= e.BalanceChanges[i].TxIdx { + return errors.New("balance changes not in ascending order by tx index") + } } // Check the nonce changes are sorted in order - if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int { - return cmp.Compare[uint32](a.TxIdx, b.TxIdx) - }) { - return errors.New("nonce changes not in ascending order by tx index") + for i := 1; i < len(e.NonceChanges); i++ { + if e.NonceChanges[i-1].TxIdx >= e.NonceChanges[i].TxIdx { + return errors.New("nonce changes not in ascending order by tx index") + } } // Check the code changes are sorted in order - if !slices.IsSortedFunc(e.CodeChanges, func(a, b encodingCodeChange) int { - return cmp.Compare[uint32](a.TxIndex, b.TxIndex) - }) { - return errors.New("code changes not in ascending order by tx index") + for i := 1; i < len(e.CodeChanges); i++ { + if e.CodeChanges[i-1].TxIndex >= e.CodeChanges[i].TxIndex { + return errors.New("code changes not in ascending order by tx index") + } } for _, change := range e.CodeChanges { var sizeLimit int diff --git a/core/types/bal/bal_test.go b/core/types/bal/bal_test.go index 32a0292f2e..0c464e84c1 100644 --- a/core/types/bal/bal_test.go +++ b/core/types/bal/bal_test.go @@ -231,6 +231,71 @@ func TestBlockAccessListCopy(t *testing.T) { } } +// TestBlockAccessListValidateDuplicateBalanceTxIdxRejects verifies that adjacent +// balance changes with equal tx indices are rejected. Ordering used to rely on +// slices.IsSortedFunc, which treats equal consecutive keys as sorted; the +// explicit loop requires strict ascent (no duplicates). +func TestBlockAccessListValidateDuplicateBalanceTxIdxRejects(t *testing.T) { + list := BlockAccessList{ + { + BalanceChanges: []encodingBalanceChange{ + {TxIdx: 1, Balance: uint256.NewInt(1)}, + {TxIdx: 1, Balance: uint256.NewInt(2)}, + }, + }, + } + err := list.Validate(params.Rules{}) + if err == nil { + t.Fatal("expected validation error for duplicate balance change tx indices") + } + want := "balance changes not in ascending order by tx index" + if got := err.Error(); got != want { + t.Fatalf("wrong error: got %q, want %q", got, want) + } +} + +// TestBlockAccessListValidateDuplicateNonceTxIdxRejects is the nonce analogue of +// TestBlockAccessListValidateDuplicateBalanceTxIdxRejects. +func TestBlockAccessListValidateDuplicateNonceTxIdxRejects(t *testing.T) { + list := BlockAccessList{ + { + NonceChanges: []encodingAccountNonce{ + {TxIdx: 2, Nonce: 10}, + {TxIdx: 2, Nonce: 11}, + }, + }, + } + err := list.Validate(params.Rules{}) + if err == nil { + t.Fatal("expected validation error for duplicate nonce change tx indices") + } + want := "nonce changes not in ascending order by tx index" + if got := err.Error(); got != want { + t.Fatalf("wrong error: got %q, want %q", got, want) + } +} + +// TestBlockAccessListValidateDuplicateCodeTxIndexRejects is the code-change +// analogue of TestBlockAccessListValidateDuplicateBalanceTxIdxRejects. +func TestBlockAccessListValidateDuplicateCodeTxIndexRejects(t *testing.T) { + list := BlockAccessList{ + { + CodeChanges: []encodingCodeChange{ + {TxIndex: 3, Code: nil}, + {TxIndex: 3, Code: []byte{0x00}}, + }, + }, + } + err := list.Validate(params.Rules{}) + if err == nil { + t.Fatal("expected validation error for duplicate code change tx indices") + } + want := "code changes not in ascending order by tx index" + if got := err.Error(); got != want { + t.Fatalf("wrong error: got %q, want %q", got, want) + } +} + func TestBlockAccessListValidation(t *testing.T) { // Validate the block access list after RLP decoding enc := makeTestBAL(true)