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.
This commit is contained in:
Weixie Cui 2026-05-07 23:03:10 +08:00
parent aaa2b66285
commit c195132aad
2 changed files with 77 additions and 12 deletions

View file

@ -180,24 +180,24 @@ func (e *AccountAccess) validate(rules params.Rules) error {
} }
// Check the balance changes are sorted in order // Check the balance changes are sorted in order
if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int { for i := 1; i < len(e.BalanceChanges); i++ {
return cmp.Compare[uint32](a.TxIdx, b.TxIdx) if e.BalanceChanges[i-1].TxIdx >= e.BalanceChanges[i].TxIdx {
}) { return errors.New("balance changes not in ascending order by tx index")
return errors.New("balance changes not in ascending order by tx index") }
} }
// Check the nonce changes are sorted in order // Check the nonce changes are sorted in order
if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int { for i := 1; i < len(e.NonceChanges); i++ {
return cmp.Compare[uint32](a.TxIdx, b.TxIdx) if e.NonceChanges[i-1].TxIdx >= e.NonceChanges[i].TxIdx {
}) { return errors.New("nonce changes not in ascending order by tx index")
return errors.New("nonce changes not in ascending order by tx index") }
} }
// Check the code changes are sorted in order // Check the code changes are sorted in order
if !slices.IsSortedFunc(e.CodeChanges, func(a, b encodingCodeChange) int { for i := 1; i < len(e.CodeChanges); i++ {
return cmp.Compare[uint32](a.TxIndex, b.TxIndex) if e.CodeChanges[i-1].TxIndex >= e.CodeChanges[i].TxIndex {
}) { return errors.New("code changes not in ascending order by tx index")
return errors.New("code changes not in ascending order by tx index") }
} }
for _, change := range e.CodeChanges { for _, change := range e.CodeChanges {
var sizeLimit int var sizeLimit int

View file

@ -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) { func TestBlockAccessListValidation(t *testing.T) {
// Validate the block access list after RLP decoding // Validate the block access list after RLP decoding
enc := makeTestBAL(true) enc := makeTestBAL(true)