Skip to content

Commit cb7eb89

Browse files
ARR4NDarioush Jalali
andauthored
fix: state.stateObject.empty() with extra payload (#62)
## Why this should be merged Fixes invariant broken by introduction of `types.StateAccountExtra`. ## How this works `state.stateObject.empty()` now also requires that the underlying account's `Extra` field carries the zero value if a type is registered, or is itself nil. ## How this was tested New unit test. --------- Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org>
1 parent 5c66352 commit cb7eb89

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

core/state/state_object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ type stateObject struct {
9393

9494
// empty returns whether the account is considered empty.
9595
func (s *stateObject) empty() bool {
96-
return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
96+
return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) && s.data.Extra.IsZero()
9797
}
9898

9999
// newObject creates a state object.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2024 the libevm authors.
2+
//
3+
// The libevm additions to go-ethereum are free software: you can redistribute
4+
// them and/or modify them under the terms of the GNU Lesser General Public License
5+
// as published by the Free Software Foundation, either version 3 of the License,
6+
// or (at your option) any later version.
7+
//
8+
// The libevm additions are distributed in the hope that they will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11+
// General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Lesser General Public License
14+
// along with the go-ethereum library. If not, see
15+
// <http://www.gnu.org/licenses/>.
16+
17+
package state
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/require"
23+
24+
"github.com/ava-labs/libevm/common"
25+
"github.com/ava-labs/libevm/core/types"
26+
)
27+
28+
func TestStateObjectEmpty(t *testing.T) {
29+
tests := []struct {
30+
name string
31+
registerAndSet func(*types.StateAccount)
32+
wantEmpty bool
33+
}{
34+
{
35+
name: "no registered types.StateAccount extra payload",
36+
registerAndSet: func(*types.StateAccount) {},
37+
wantEmpty: true,
38+
},
39+
{
40+
name: "erroneously non-nil types.StateAccountExtra when no registered payload",
41+
registerAndSet: func(acc *types.StateAccount) {
42+
acc.Extra = &types.StateAccountExtra{}
43+
},
44+
wantEmpty: true,
45+
},
46+
{
47+
name: "explicit false bool",
48+
registerAndSet: func(acc *types.StateAccount) {
49+
types.RegisterExtras[bool]().SetOnStateAccount(acc, false)
50+
},
51+
wantEmpty: true,
52+
},
53+
{
54+
name: "implicit false bool",
55+
registerAndSet: func(*types.StateAccount) {
56+
types.RegisterExtras[bool]()
57+
},
58+
wantEmpty: true,
59+
},
60+
{
61+
name: "true bool",
62+
registerAndSet: func(acc *types.StateAccount) {
63+
types.RegisterExtras[bool]().SetOnStateAccount(acc, true)
64+
},
65+
wantEmpty: false,
66+
},
67+
}
68+
69+
for _, tt := range tests {
70+
t.Run(tt.name, func(t *testing.T) {
71+
types.TestOnlyClearRegisteredExtras()
72+
t.Cleanup(types.TestOnlyClearRegisteredExtras)
73+
74+
obj := newObject(nil, common.Address{}, nil)
75+
tt.registerAndSet(&obj.data)
76+
require.Equalf(t, tt.wantEmpty, obj.empty(), "%T.empty()", obj)
77+
})
78+
}
79+
}

core/types/rlp_payload.libevm.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ func (e *StateAccountExtra) Equal(f *StateAccountExtra) bool {
160160
return e.t.Equal(f.t)
161161
}
162162

163+
// IsZero reports whether e carries the the zero value for its type, as
164+
// registered via [RegisterExtras]. It returns true if no type was registered or
165+
// if `e == nil`.
166+
func (e *StateAccountExtra) IsZero() bool {
167+
return e == nil || e.t == nil || e.t.IsZero()
168+
}
169+
163170
var _ interface {
164171
rlp.Encoder
165172
rlp.Decoder

0 commit comments

Comments
 (0)