mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-16 18:00:46 +00:00
feat(core/types): body RLP hooks registration (#130)
Allow to register body extras in consumers of libevm. Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
This commit is contained in:
parent
80fbed641e
commit
b2c38ce397
10 changed files with 134 additions and 57 deletions
|
|
@ -45,7 +45,11 @@ func TestGetSetExtra(t *testing.T) {
|
||||||
t.Cleanup(types.TestOnlyClearRegisteredExtras)
|
t.Cleanup(types.TestOnlyClearRegisteredExtras)
|
||||||
// Just as its Data field is a pointer, the registered type is a pointer to
|
// Just as its Data field is a pointer, the registered type is a pointer to
|
||||||
// test deep copying.
|
// test deep copying.
|
||||||
payloads := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, *accountExtra]().StateAccount
|
payloads := types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
*accountExtra,
|
||||||
|
]().StateAccount
|
||||||
|
|
||||||
rng := ethtest.NewPseudoRand(42)
|
rng := ethtest.NewPseudoRand(42)
|
||||||
addr := rng.Address()
|
addr := rng.Address()
|
||||||
|
|
|
||||||
|
|
@ -46,21 +46,33 @@ func TestStateObjectEmpty(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "explicit false bool",
|
name: "explicit false bool",
|
||||||
registerAndSet: func(acc *types.StateAccount) {
|
registerAndSet: func(acc *types.StateAccount) {
|
||||||
types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]().StateAccount.Set(acc, false)
|
types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]().StateAccount.Set(acc, false)
|
||||||
},
|
},
|
||||||
wantEmpty: true,
|
wantEmpty: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "implicit false bool",
|
name: "implicit false bool",
|
||||||
registerAndSet: func(*types.StateAccount) {
|
registerAndSet: func(*types.StateAccount) {
|
||||||
types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]()
|
types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
},
|
},
|
||||||
wantEmpty: true,
|
wantEmpty: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "true bool",
|
name: "true bool",
|
||||||
registerAndSet: func(acc *types.StateAccount) {
|
registerAndSet: func(acc *types.StateAccount) {
|
||||||
types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]().StateAccount.Set(acc, true)
|
types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]().StateAccount.Set(acc, true)
|
||||||
},
|
},
|
||||||
wantEmpty: false,
|
wantEmpty: false,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/kr/pretty"
|
"github.com/kr/pretty"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -56,14 +57,18 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
for _, tx := range txMatrix {
|
for _, tx := range txMatrix {
|
||||||
for _, u := range uncleMatrix {
|
for _, u := range uncleMatrix {
|
||||||
for _, w := range withdrawMatrix {
|
for _, w := range withdrawMatrix {
|
||||||
bodies = append(bodies, &Body{tx, u, w})
|
bodies = append(bodies, &Body{tx, u, w, nil /* extra field */})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, body := range bodies {
|
for _, body := range bodies {
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
t.Logf("\n%s", pretty.Sprint(body))
|
t.Cleanup(func() {
|
||||||
|
if t.Failed() {
|
||||||
|
t.Logf("\n%s", pretty.Sprint(body))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// The original [Body] doesn't implement [rlp.Encoder] nor
|
// The original [Body] doesn't implement [rlp.Encoder] nor
|
||||||
// [rlp.Decoder] so we can use a methodless equivalent as the gold
|
// [rlp.Decoder] so we can use a methodless equivalent as the gold
|
||||||
|
|
@ -74,14 +79,15 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Encode", func(t *testing.T) {
|
t.Run("Encode", func(t *testing.T) {
|
||||||
got, err := rlp.EncodeToBytes(body)
|
got, err := rlp.EncodeToBytes(body)
|
||||||
require.NoErrorf(t, err, "rlp.EncodeToBytes(%#v)", body)
|
require.NoErrorf(t, err, "rlp.EncodeToBytes(%T)", body)
|
||||||
assert.Equalf(t, wantRLP, got, "rlp.EncodeToBytes(%#v)", body)
|
assert.Equalf(t, wantRLP, got, "rlp.EncodeToBytes(%T)", body)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Decode", func(t *testing.T) {
|
t.Run("Decode", func(t *testing.T) {
|
||||||
got := new(Body)
|
got := new(Body)
|
||||||
err := rlp.DecodeBytes(wantRLP, got)
|
err := rlp.DecodeBytes(wantRLP, got)
|
||||||
require.NoErrorf(t, err, "rlp.DecodeBytes(%v, %T)", wantRLP, got)
|
require.NoErrorf(t, err, "rlp.DecodeBytes(rlp.EncodeToBytes(%T), %T) resulted in %s",
|
||||||
|
(*withoutMethods)(body), got, pretty.Sprint(got))
|
||||||
|
|
||||||
want := body
|
want := body
|
||||||
// Regular RLP decoding will never leave these non-optional
|
// Regular RLP decoding will never leave these non-optional
|
||||||
|
|
@ -96,9 +102,10 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
opts := cmp.Options{
|
opts := cmp.Options{
|
||||||
cmp.Comparer((*Header).equalHash),
|
cmp.Comparer((*Header).equalHash),
|
||||||
cmp.Comparer((*Transaction).equalHash),
|
cmp.Comparer((*Transaction).equalHash),
|
||||||
|
cmpopts.IgnoreUnexported(Body{}),
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(body, got, opts); diff != "" {
|
if diff := cmp.Diff(want, got, opts); diff != "" {
|
||||||
t.Errorf("rlp.DecodeBytes(rlp.EncodeToBytes(%#v)) diff (-want +got):\n%s", body, diff)
|
t.Errorf("rlp.DecodeBytes(rlp.EncodeToBytes(%T)) diff (-want +got):\n%s", (*withoutMethods)(body), diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -148,10 +155,13 @@ func TestBodyRLPCChainCompat(t *testing.T) {
|
||||||
// The inputs to this test were used to generate the expected RLP with
|
// The inputs to this test were used to generate the expected RLP with
|
||||||
// ava-labs/coreth. This serves as both an example of how to use [BodyHooks]
|
// ava-labs/coreth. This serves as both an example of how to use [BodyHooks]
|
||||||
// and a test of compatibility.
|
// and a test of compatibility.
|
||||||
|
TestOnlyClearRegisteredExtras()
|
||||||
t.Cleanup(func() {
|
t.Cleanup(TestOnlyClearRegisteredExtras)
|
||||||
TestOnlyRegisterBodyHooks(NOOPBodyHooks{})
|
extras := RegisterExtras[
|
||||||
})
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
cChainBodyExtras, *cChainBodyExtras,
|
||||||
|
struct{},
|
||||||
|
]()
|
||||||
|
|
||||||
body := &Body{
|
body := &Body{
|
||||||
Transactions: []*Transaction{
|
Transactions: []*Transaction{
|
||||||
|
|
@ -194,7 +204,7 @@ func TestBodyRLPCChainCompat(t *testing.T) {
|
||||||
require.NoErrorf(t, err, "hex.DecodeString(%q)", tt.wantRLPHex)
|
require.NoErrorf(t, err, "hex.DecodeString(%q)", tt.wantRLPHex)
|
||||||
|
|
||||||
t.Run("Encode", func(t *testing.T) {
|
t.Run("Encode", func(t *testing.T) {
|
||||||
TestOnlyRegisterBodyHooks(tt.extra)
|
extras.Body.Set(body, tt.extra)
|
||||||
got, err := rlp.EncodeToBytes(body)
|
got, err := rlp.EncodeToBytes(body)
|
||||||
require.NoErrorf(t, err, "rlp.EncodeToBytes(%+v)", body)
|
require.NoErrorf(t, err, "rlp.EncodeToBytes(%+v)", body)
|
||||||
assert.Equalf(t, wantRLP, got, "rlp.EncodeToBytes(%+v)", body)
|
assert.Equalf(t, wantRLP, got, "rlp.EncodeToBytes(%+v)", body)
|
||||||
|
|
@ -202,9 +212,8 @@ func TestBodyRLPCChainCompat(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Decode", func(t *testing.T) {
|
t.Run("Decode", func(t *testing.T) {
|
||||||
var extra cChainBodyExtras
|
var extra cChainBodyExtras
|
||||||
TestOnlyRegisterBodyHooks(&extra)
|
|
||||||
|
|
||||||
got := new(Body)
|
got := new(Body)
|
||||||
|
extras.Body.Set(got, &extra)
|
||||||
err := rlp.DecodeBytes(wantRLP, got)
|
err := rlp.DecodeBytes(wantRLP, got)
|
||||||
require.NoErrorf(t, err, "rlp.DecodeBytes(%#x, %T)", wantRLP, got)
|
require.NoErrorf(t, err, "rlp.DecodeBytes(%#x, %T)", wantRLP, got)
|
||||||
assert.Equal(t, tt.extra, &extra, "rlp.DecodeBytes(%#x, [%T as registered extra in %T carrier])", wantRLP, &extra, got)
|
assert.Equal(t, tt.extra, &extra, "rlp.DecodeBytes(%#x, [%T as registered extra in %T carrier])", wantRLP, &extra, got)
|
||||||
|
|
@ -212,6 +221,7 @@ func TestBodyRLPCChainCompat(t *testing.T) {
|
||||||
opts := cmp.Options{
|
opts := cmp.Options{
|
||||||
cmp.Comparer((*Header).equalHash),
|
cmp.Comparer((*Header).equalHash),
|
||||||
cmp.Comparer((*Transaction).equalHash),
|
cmp.Comparer((*Transaction).equalHash),
|
||||||
|
cmpopts.IgnoreUnexported(Body{}),
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(body, got, opts); diff != "" {
|
if diff := cmp.Diff(body, got, opts); diff != "" {
|
||||||
t.Errorf("rlp.DecodeBytes(%#x, [%T while carrying registered %T extra payload]) diff (-want +got):\n%s", wantRLP, got, &extra, diff)
|
t.Errorf("rlp.DecodeBytes(%#x, [%T while carrying registered %T extra payload]) diff (-want +got):\n%s", wantRLP, got, &extra, diff)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,11 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "no-op header hooks",
|
name: "no-op header hooks",
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, struct{}]()
|
RegisterExtras[
|
||||||
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
NOOPBodyHooks, *NOOPBodyHooks,
|
||||||
|
struct{},
|
||||||
|
]()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,8 @@ type Body struct {
|
||||||
Transactions []*Transaction
|
Transactions []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
|
||||||
|
extra *pseudo.Type // See [RegisterExtras]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block represents an Ethereum block.
|
// Block represents an Ethereum block.
|
||||||
|
|
@ -338,7 +340,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
// Body returns the non-header content of the block.
|
// Body returns the non-header content of the block.
|
||||||
// Note the returned data is not an independent copy.
|
// Note the returned data is not an independent copy.
|
||||||
func (b *Block) Body() *Body {
|
func (b *Block) Body() *Body {
|
||||||
return &Body{b.transactions, b.uncles, b.withdrawals}
|
return &Body{b.transactions, b.uncles, b.withdrawals, nil /* unexported extras field */}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessors for body data. These do not return a copy because the content
|
// Accessors for body data. These do not return a copy because the content
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ava-labs/libevm/libevm/pseudo"
|
"github.com/ava-labs/libevm/libevm/pseudo"
|
||||||
"github.com/ava-labs/libevm/libevm/testonly"
|
|
||||||
"github.com/ava-labs/libevm/rlp"
|
"github.com/ava-labs/libevm/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -45,7 +44,7 @@ func (h *Header) hooks() HeaderHooks {
|
||||||
return new(NOOPHeaderHooks)
|
return new(NOOPHeaderHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ExtraPayloads[HPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
|
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromHeader(h *Header) HeaderHooks {
|
||||||
return e.Header.Get(h)
|
return e.Header.Get(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,22 +133,11 @@ type BodyHooks interface {
|
||||||
RLPFieldPointersForDecoding(*Body) *rlp.Fields
|
RLPFieldPointersForDecoding(*Body) *rlp.Fields
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestOnlyRegisterBodyHooks is a temporary means of "registering" BodyHooks for
|
|
||||||
// the purpose of testing. It will panic if called outside of a test.
|
|
||||||
func TestOnlyRegisterBodyHooks(h BodyHooks) {
|
|
||||||
testonly.OrPanic(func() {
|
|
||||||
todoRegisteredBodyHooks = h
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// todoRegisteredBodyHooks is a temporary placeholder for "registering"
|
|
||||||
// BodyHooks, before they are included in [RegisterExtras].
|
|
||||||
var todoRegisteredBodyHooks BodyHooks = NOOPBodyHooks{}
|
|
||||||
|
|
||||||
func (b *Body) hooks() BodyHooks {
|
func (b *Body) hooks() BodyHooks {
|
||||||
// TODO(arr4n): when incorporating BodyHooks into [RegisterExtras], the
|
if r := registeredExtras; r.Registered() {
|
||||||
// [todoRegisteredBodyHooks] variable MUST be removed.
|
return r.Get().hooks.hooksFromBody(b)
|
||||||
return todoRegisteredBodyHooks
|
}
|
||||||
|
return NOOPBodyHooks{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOOPBodyHooks implements [BodyHooks] such that they are equivalent to no type
|
// NOOPBodyHooks implements [BodyHooks] such that they are equivalent to no type
|
||||||
|
|
@ -160,7 +148,7 @@ type NOOPBodyHooks struct{}
|
||||||
// fields and their order, which we lock in here as a change detector. If this
|
// fields and their order, which we lock in here as a change detector. If this
|
||||||
// breaks then it MUST be updated and the RLP methods reviewed + new
|
// breaks then it MUST be updated and the RLP methods reviewed + new
|
||||||
// backwards-compatibility tests added.
|
// backwards-compatibility tests added.
|
||||||
var _ = &Body{[]*Transaction{}, []*Header{}, []*Withdrawal{}}
|
var _ = &Body{[]*Transaction{}, []*Header{}, []*Withdrawal{}, nil /* extra unexported type */}
|
||||||
|
|
||||||
func (NOOPBodyHooks) RLPFieldsForEncoding(b *Body) *rlp.Fields {
|
func (NOOPBodyHooks) RLPFieldsForEncoding(b *Body) *rlp.Fields {
|
||||||
return &rlp.Fields{
|
return &rlp.Fields{
|
||||||
|
|
@ -175,3 +163,19 @@ func (NOOPBodyHooks) RLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
||||||
Optional: []any{&b.Withdrawals},
|
Optional: []any{&b.Withdrawals},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromBody(b *Body) BodyHooks {
|
||||||
|
return e.Body.Get(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Body) extraPayload() *pseudo.Type {
|
||||||
|
r := registeredExtras
|
||||||
|
if !r.Registered() {
|
||||||
|
// See params.ChainConfig.extraPayload() for panic rationale.
|
||||||
|
panic(fmt.Sprintf("%T.extraPayload() called before RegisterExtras()", r))
|
||||||
|
}
|
||||||
|
if b.extra == nil {
|
||||||
|
b.extra = r.Get().newBody()
|
||||||
|
}
|
||||||
|
return b.extra
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,11 @@ func TestHeaderHooks(t *testing.T) {
|
||||||
TestOnlyClearRegisteredExtras()
|
TestOnlyClearRegisteredExtras()
|
||||||
defer TestOnlyClearRegisteredExtras()
|
defer TestOnlyClearRegisteredExtras()
|
||||||
|
|
||||||
extras := RegisterExtras[stubHeaderHooks, *stubHeaderHooks, struct{}]()
|
extras := RegisterExtras[
|
||||||
|
stubHeaderHooks, *stubHeaderHooks,
|
||||||
|
NOOPBodyHooks, *NOOPBodyHooks,
|
||||||
|
struct{},
|
||||||
|
]()
|
||||||
rng := ethtest.NewPseudoRand(13579)
|
rng := ethtest.NewPseudoRand(13579)
|
||||||
|
|
||||||
suffix := rng.Bytes(8)
|
suffix := rng.Bytes(8)
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,21 @@ func RegisterExtras[
|
||||||
HeaderHooks
|
HeaderHooks
|
||||||
*H
|
*H
|
||||||
},
|
},
|
||||||
|
B any, BPtr interface {
|
||||||
|
BodyHooks
|
||||||
|
*B
|
||||||
|
},
|
||||||
SA any,
|
SA any,
|
||||||
]() ExtraPayloads[HPtr, SA] {
|
]() ExtraPayloads[HPtr, BPtr, SA] {
|
||||||
extra := ExtraPayloads[HPtr, SA]{
|
extra := ExtraPayloads[HPtr, BPtr, SA]{
|
||||||
Header: pseudo.NewAccessor[*Header, HPtr](
|
Header: pseudo.NewAccessor[*Header, HPtr](
|
||||||
(*Header).extraPayload,
|
(*Header).extraPayload,
|
||||||
func(h *Header, t *pseudo.Type) { h.extra = t },
|
func(h *Header, t *pseudo.Type) { h.extra = t },
|
||||||
),
|
),
|
||||||
|
Body: pseudo.NewAccessor[*Body, BPtr](
|
||||||
|
(*Body).extraPayload,
|
||||||
|
func(b *Body, t *pseudo.Type) { b.extra = t },
|
||||||
|
),
|
||||||
StateAccount: pseudo.NewAccessor[StateOrSlimAccount, SA](
|
StateAccount: pseudo.NewAccessor[StateOrSlimAccount, SA](
|
||||||
func(a StateOrSlimAccount) *pseudo.Type { return a.extra().payload() },
|
func(a StateOrSlimAccount) *pseudo.Type { return a.extra().payload() },
|
||||||
func(a StateOrSlimAccount, t *pseudo.Type) { a.extra().t = t },
|
func(a StateOrSlimAccount, t *pseudo.Type) { a.extra().t = t },
|
||||||
|
|
@ -63,10 +71,11 @@ func RegisterExtras[
|
||||||
var x SA
|
var x SA
|
||||||
return fmt.Sprintf("%T", x)
|
return fmt.Sprintf("%T", x)
|
||||||
}(),
|
}(),
|
||||||
// The [ExtraPayloads] that we returns is based on [HPtr,SA], not [H,SA]
|
// The [ExtraPayloads] that we returns is based on [HPtr,BPtr,SA], not
|
||||||
// so our constructors MUST match that. This guarantees that calls to
|
// [H,B,SA] so our constructors MUST match that. This guarantees that calls to
|
||||||
// the [HeaderHooks] methods will never be performed on a nil pointer.
|
// the [HeaderHooks] and [BodyHooks] methods will never be performed on a nil pointer.
|
||||||
newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr
|
newHeader: pseudo.NewConstructor[H]().NewPointer, // i.e. non-nil HPtr
|
||||||
|
newBody: pseudo.NewConstructor[B]().NewPointer, // i.e. non-nil BPtr
|
||||||
newStateAccount: pseudo.NewConstructor[SA]().Zero,
|
newStateAccount: pseudo.NewConstructor[SA]().Zero,
|
||||||
cloneStateAccount: extra.cloneStateAccount,
|
cloneStateAccount: extra.cloneStateAccount,
|
||||||
hooks: extra,
|
hooks: extra,
|
||||||
|
|
@ -87,11 +96,14 @@ func TestOnlyClearRegisteredExtras() {
|
||||||
var registeredExtras register.AtMostOnce[*extraConstructors]
|
var registeredExtras register.AtMostOnce[*extraConstructors]
|
||||||
|
|
||||||
type extraConstructors struct {
|
type extraConstructors struct {
|
||||||
stateAccountType string
|
stateAccountType string
|
||||||
newHeader, newStateAccount func() *pseudo.Type
|
newHeader func() *pseudo.Type
|
||||||
cloneStateAccount func(*StateAccountExtra) *StateAccountExtra
|
newBody func() *pseudo.Type
|
||||||
hooks interface {
|
newStateAccount func() *pseudo.Type
|
||||||
|
cloneStateAccount func(*StateAccountExtra) *StateAccountExtra
|
||||||
|
hooks interface {
|
||||||
hooksFromHeader(*Header) HeaderHooks
|
hooksFromHeader(*Header) HeaderHooks
|
||||||
|
hooksFromBody(*Body) BodyHooks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,14 +117,15 @@ func (e *StateAccountExtra) clone() *StateAccountExtra {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtraPayloads provides strongly typed access to the extra payload carried by
|
// ExtraPayloads provides strongly typed access to the extra payload carried by
|
||||||
// [Header], [StateAccount], and [SlimAccount] structs. The only valid way to
|
// [Header], [Body], [StateAccount], and [SlimAccount] structs. The only valid way to
|
||||||
// construct an instance is by a call to [RegisterExtras].
|
// construct an instance is by a call to [RegisterExtras].
|
||||||
type ExtraPayloads[HPtr HeaderHooks, SA any] struct {
|
type ExtraPayloads[HPtr HeaderHooks, BPtr BodyHooks, SA any] struct {
|
||||||
Header pseudo.Accessor[*Header, HPtr]
|
Header pseudo.Accessor[*Header, HPtr]
|
||||||
|
Body pseudo.Accessor[*Body, BPtr]
|
||||||
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
|
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ExtraPayloads[HPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
|
func (ExtraPayloads[HPtr, BPtr, SA]) cloneStateAccount(s *StateAccountExtra) *StateAccountExtra {
|
||||||
v := pseudo.MustNewValue[SA](s.t)
|
v := pseudo.MustNewValue[SA](s.t)
|
||||||
return &StateAccountExtra{
|
return &StateAccountExtra{
|
||||||
t: pseudo.From(v.Get()).Type,
|
t: pseudo.From(v.Get()).Type,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,11 @@ func TestStateAccountRLP(t *testing.T) {
|
||||||
explicitFalseBoolean := test{
|
explicitFalseBoolean := test{
|
||||||
name: "explicit false-boolean extra",
|
name: "explicit false-boolean extra",
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, bool]()
|
RegisterExtras[
|
||||||
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
NOOPBodyHooks, *NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
},
|
},
|
||||||
acc: &StateAccount{
|
acc: &StateAccount{
|
||||||
Nonce: 0x444444,
|
Nonce: 0x444444,
|
||||||
|
|
@ -76,7 +80,11 @@ func TestStateAccountRLP(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "true-boolean extra",
|
name: "true-boolean extra",
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[NOOPHeaderHooks, *NOOPHeaderHooks, bool]()
|
RegisterExtras[
|
||||||
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
NOOPBodyHooks, *NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
},
|
},
|
||||||
acc: &StateAccount{
|
acc: &StateAccount{
|
||||||
Nonce: 0x444444,
|
Nonce: 0x444444,
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,11 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "true-boolean payload",
|
name: "true-boolean payload",
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]()
|
e := types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
e.StateAccount.Set(a, true)
|
e.StateAccount.Set(a, true)
|
||||||
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
||||||
assert.Truef(t, e.StateAccount.Get(got), "")
|
assert.Truef(t, e.StateAccount.Get(got), "")
|
||||||
|
|
@ -84,7 +88,11 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "explicit false-boolean payload",
|
name: "explicit false-boolean payload",
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]()
|
e := types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
e.StateAccount.Set(a, false) // the explicit part
|
e.StateAccount.Set(a, false) // the explicit part
|
||||||
|
|
||||||
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
||||||
|
|
@ -96,7 +104,11 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "implicit false-boolean payload",
|
name: "implicit false-boolean payload",
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, bool]()
|
e := types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
bool,
|
||||||
|
]()
|
||||||
// Note that `a` is reflected, unchanged (the implicit part).
|
// Note that `a` is reflected, unchanged (the implicit part).
|
||||||
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
||||||
assert.Falsef(t, e.StateAccount.Get(got), "")
|
assert.Falsef(t, e.StateAccount.Get(got), "")
|
||||||
|
|
@ -107,7 +119,11 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "arbitrary payload",
|
name: "arbitrary payload",
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[types.NOOPHeaderHooks, *types.NOOPHeaderHooks, arbitraryPayload]()
|
e := types.RegisterExtras[
|
||||||
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
|
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
||||||
|
arbitraryPayload,
|
||||||
|
]()
|
||||||
p := arbitraryPayload{arbitraryData}
|
p := arbitraryPayload{arbitraryData}
|
||||||
e.StateAccount.Set(a, p)
|
e.StateAccount.Set(a, p)
|
||||||
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
return a, func(t *testing.T, got *types.StateAccount) { //nolint:thelper
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue