Skip to content

Commit e2e9330

Browse files
committed
Merge branch 'libevm' into arr4n/jumptable-override
2 parents f122da5 + 51cd795 commit e2e9330

23 files changed

+1263
-93
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ jobs:
1818
go-version: 1.21.4
1919
- name: Run tests
2020
run: | # Upstream flakes are race conditions exacerbated by concurrent tests
21-
FLAKY_REGEX='go-ethereum/(eth|accounts/keystore|eth/downloader|miner|ethclient|ethclient/gethclient|eth/catalyst)$';
21+
FLAKY_REGEX='go-ethereum/(eth|eth/tracers/js|eth/tracers/logger|accounts/keystore|eth/downloader|miner|ethclient|ethclient/gethclient|eth/catalyst)$';
2222
go list ./... | grep -P "${FLAKY_REGEX}" | xargs -n 1 go test -short;
2323
go test -short $(go list ./... | grep -Pv "${FLAKY_REGEX}");

core/state/statedb.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
579579
Balance: acc.Balance,
580580
CodeHash: acc.CodeHash,
581581
Root: common.BytesToHash(acc.Root),
582+
Extra: acc.Extra, // no need to deep-copy as `acc` is short-lived
582583
}
583584
if len(data.CodeHash) == 0 {
584585
data.CodeHash = types.EmptyCodeHash.Bytes()

core/types/gen_account_rlp.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/gen_slim_account_rlp.libevm.go

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types/rlp_payload.libevm.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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 types
18+
19+
import (
20+
"fmt"
21+
"io"
22+
23+
"github.com/ethereum/go-ethereum/libevm/pseudo"
24+
"github.com/ethereum/go-ethereum/libevm/testonly"
25+
"github.com/ethereum/go-ethereum/rlp"
26+
)
27+
28+
// RegisterExtras registers the type `SA` to be carried as an extra payload in
29+
// [StateAccount] structs. It is expected to be called in an `init()` function
30+
// and MUST NOT be called more than once.
31+
//
32+
// The payload will be treated as an extra struct field for the purposes of RLP
33+
// encoding and decoding. RLP handling is plumbed through to the `SA` via the
34+
// [StateAccountExtra] that holds it such that it acts as if there were a field
35+
// of type `SA` in all StateAccount structs.
36+
//
37+
// The payload can be acced via the [ExtraPayloads.FromStateAccount] method of
38+
// the accessor returned by RegisterExtras.
39+
func RegisterExtras[SA any]() ExtraPayloads[SA] {
40+
if registeredExtras != nil {
41+
panic("re-registration of Extras")
42+
}
43+
var extra ExtraPayloads[SA]
44+
registeredExtras = &extraConstructors{
45+
stateAccountType: func() string {
46+
var x SA
47+
return fmt.Sprintf("%T", x)
48+
}(),
49+
newStateAccount: pseudo.NewConstructor[SA]().Zero,
50+
cloneStateAccount: extra.cloneStateAccount,
51+
}
52+
return extra
53+
}
54+
55+
// TestOnlyClearRegisteredExtras clears the [Extras] previously passed to
56+
// [RegisterExtras]. It panics if called from a non-testing call stack.
57+
//
58+
// In tests it SHOULD be called before every call to [RegisterExtras] and then
59+
// defer-called afterwards, either directly or via testing.TB.Cleanup(). This is
60+
// a workaround for the single-call limitation on [RegisterExtras].
61+
func TestOnlyClearRegisteredExtras() {
62+
testonly.OrPanic(func() {
63+
registeredExtras = nil
64+
})
65+
}
66+
67+
var registeredExtras *extraConstructors
68+
69+
type extraConstructors struct {
70+
stateAccountType string
71+
newStateAccount func() *pseudo.Type
72+
cloneStateAccount func(*StateAccountExtra) *StateAccountExtra
73+
}
74+
75+
func (e *StateAccountExtra) clone() *StateAccountExtra {
76+
switch r := registeredExtras; {
77+
case r == nil, e == nil:
78+
return nil
79+
default:
80+
return r.cloneStateAccount(e)
81+
}
82+
}
83+
84+
// ExtraPayloads provides strongly typed access to the extra payload carried by
85+
// [StateAccount] structs. The only valid way to construct an instance is by a
86+
// call to [RegisterExtras].
87+
type ExtraPayloads[SA any] struct {
88+
_ struct{} // make godoc show unexported fields so nobody tries to make their own instance ;)
89+
}
90+
91+
func (ExtraPayloads[SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
92+
v := pseudo.MustNewValue[SA](s.t)
93+
return &StateAccountExtra{
94+
t: pseudo.From(v.Get()).Type,
95+
}
96+
}
97+
98+
// FromStateAccount returns the StateAccount's payload.
99+
func (ExtraPayloads[SA]) FromStateAccount(a *StateAccount) SA {
100+
return pseudo.MustNewValue[SA](a.extra().payload()).Get()
101+
}
102+
103+
// PointerFromStateAccount returns a pointer to the StateAccounts's extra
104+
// payload. This is guaranteed to be non-nil.
105+
//
106+
// Note that copying a StateAccount by dereferencing a pointer will result in a
107+
// shallow copy and that the *SA returned here will therefore be shared by all
108+
// copies. If this is not the desired behaviour, use
109+
// [StateAccount.Copy] or [ExtraPayloads.SetOnStateAccount].
110+
func (ExtraPayloads[SA]) PointerFromStateAccount(a *StateAccount) *SA {
111+
return pseudo.MustPointerTo[SA](a.extra().payload()).Value.Get()
112+
}
113+
114+
// SetOnStateAccount sets the StateAccount's payload.
115+
func (ExtraPayloads[SA]) SetOnStateAccount(a *StateAccount, val SA) {
116+
a.extra().t = pseudo.From(val).Type
117+
}
118+
119+
// A StateAccountExtra carries the extra payload, if any, registered with
120+
// [RegisterExtras]. It SHOULD NOT be used directly; instead use the
121+
// [ExtraPayloads] accessor returned by RegisterExtras.
122+
type StateAccountExtra struct {
123+
t *pseudo.Type
124+
}
125+
126+
func (a *StateAccount) extra() *StateAccountExtra {
127+
if a.Extra == nil {
128+
a.Extra = &StateAccountExtra{
129+
t: registeredExtras.newStateAccount(),
130+
}
131+
}
132+
return a.Extra
133+
}
134+
135+
func (e *StateAccountExtra) payload() *pseudo.Type {
136+
if e.t == nil {
137+
e.t = registeredExtras.newStateAccount()
138+
}
139+
return e.t
140+
}
141+
142+
// Equal reports whether `e` is semantically equivalent to `f` for the purpose
143+
// of tests.
144+
//
145+
// Equal MUST NOT be used in production. Instead, compare values returned by
146+
// [ExtraPayloads.FromStateAccount].
147+
func (e *StateAccountExtra) Equal(f *StateAccountExtra) bool {
148+
if false {
149+
// TODO(arr4n): calling this results in an error from cmp.Diff():
150+
// "non-deterministic or non-symmetric function detected". Explore the
151+
// issue and then enable the enforcement.
152+
testonly.OrPanic(func() {})
153+
}
154+
155+
eNil := e == nil || e.t == nil
156+
fNil := f == nil || f.t == nil
157+
if eNil && fNil || eNil && f.t.IsZero() || fNil && e.t.IsZero() {
158+
return true
159+
}
160+
return e.t.Equal(f.t)
161+
}
162+
163+
var _ interface {
164+
rlp.Encoder
165+
rlp.Decoder
166+
fmt.Formatter
167+
} = (*StateAccountExtra)(nil)
168+
169+
// EncodeRLP implements the [rlp.Encoder] interface.
170+
func (e *StateAccountExtra) EncodeRLP(w io.Writer) error {
171+
switch r := registeredExtras; {
172+
case r == nil:
173+
return nil
174+
case e == nil:
175+
e = &StateAccountExtra{}
176+
fallthrough
177+
case e.t == nil:
178+
e.t = r.newStateAccount()
179+
}
180+
return e.t.EncodeRLP(w)
181+
}
182+
183+
// DecodeRLP implements the [rlp.Decoder] interface.
184+
func (e *StateAccountExtra) DecodeRLP(s *rlp.Stream) error {
185+
switch r := registeredExtras; {
186+
case r == nil:
187+
return nil
188+
case e.t == nil:
189+
e.t = r.newStateAccount()
190+
fallthrough
191+
default:
192+
return s.Decode(e.t)
193+
}
194+
}
195+
196+
// Format implements the [fmt.Formatter] interface.
197+
func (e *StateAccountExtra) Format(s fmt.State, verb rune) {
198+
var out string
199+
switch r := registeredExtras; {
200+
case r == nil:
201+
out = "<nil>"
202+
case e == nil, e.t == nil:
203+
out = fmt.Sprintf("<nil>[*StateAccountExtra[%s]]", r.stateAccountType)
204+
default:
205+
e.t.Format(s, verb)
206+
return
207+
}
208+
_, _ = s.Write([]byte(out))
209+
}

core/types/state_account.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
)
2626

2727
//go:generate go run ../../rlp/rlpgen -type StateAccount -out gen_account_rlp.go
28+
//go:generate go run ../../rlp/rlpgen -type SlimAccount -out gen_slim_account_rlp.libevm.go
2829

2930
// StateAccount is the Ethereum consensus representation of accounts.
3031
// These objects are stored in the main account trie.
@@ -33,6 +34,8 @@ type StateAccount struct {
3334
Balance *uint256.Int
3435
Root common.Hash // merkle root of the storage trie
3536
CodeHash []byte
37+
38+
Extra *StateAccountExtra
3639
}
3740

3841
// NewEmptyStateAccount constructs an empty state account.
@@ -55,6 +58,7 @@ func (acct *StateAccount) Copy() *StateAccount {
5558
Balance: balance,
5659
Root: acct.Root,
5760
CodeHash: common.CopyBytes(acct.CodeHash),
61+
Extra: acct.Extra.clone(),
5862
}
5963
}
6064

@@ -66,21 +70,24 @@ type SlimAccount struct {
6670
Balance *uint256.Int
6771
Root []byte // Nil if root equals to types.EmptyRootHash
6872
CodeHash []byte // Nil if hash equals to types.EmptyCodeHash
73+
74+
Extra *StateAccountExtra
6975
}
7076

7177
// SlimAccountRLP encodes the state account in 'slim RLP' format.
7278
func SlimAccountRLP(account StateAccount) []byte {
7379
slim := SlimAccount{
7480
Nonce: account.Nonce,
7581
Balance: account.Balance,
82+
Extra: account.Extra,
7683
}
7784
if account.Root != EmptyRootHash {
7885
slim.Root = account.Root[:]
7986
}
8087
if !bytes.Equal(account.CodeHash, EmptyCodeHash[:]) {
8188
slim.CodeHash = account.CodeHash
8289
}
83-
data, err := rlp.EncodeToBytes(slim)
90+
data, err := rlp.EncodeToBytes(&slim)
8491
if err != nil {
8592
panic(err)
8693
}
@@ -96,6 +103,7 @@ func FullAccount(data []byte) (*StateAccount, error) {
96103
}
97104
var account StateAccount
98105
account.Nonce, account.Balance = slim.Nonce, slim.Balance
106+
account.Extra = slim.Extra
99107

100108
// Interpret the storage root and code hash in slim format.
101109
if len(slim.Root) == 0 {

0 commit comments

Comments
 (0)