mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 02:10:46 +00:00
feat(core/types): Block RLP overriding (#133)
## Why this should be merged Support for configurable `core/types.Block` with RLP encoding, including interplay with `Body`. ## How this works `Block` doesn't export most of its fields so relies on an internal type, `extblock`, for RLP encoding. This type is modified to implement the `rlp.Encoder` and `Decoder` methods as a point to inject hooks using `rlp.Fields` (as in #120 for `Body`). `Block` shares the same registered extra type as `Body`. Unlike `Header`, which has its own field in a `Block`, the fields in `Body` are promoted to be carried directly. This suggests that (at least for pure data payloads) the modifications might be equivalent (and `ava-labs/coreth` evidences this). Should different payloads be absolutely required in the future, we can split the types—the `RegisterExtras` signature is already too verbose though 😢. ## How this was tested Explicit inclusion of a backwards-compatibility test for `NOOPBlockBodyHooks` + implicit testing via the multiple upstream tests in `block_test.go`. Re implicit testing: default behaviour is now to use the noop hooks even when no registration is performed, but if we change this then the tests in `block_test.go` can still be called as subtests from a test that explicitly registers noops. --------- Signed-off-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Co-authored-by: Quentin McGaw <quentin.mcgaw@avalabs.org>
This commit is contained in:
parent
0eb029ad49
commit
3ab3cd2c2b
10 changed files with 354 additions and 65 deletions
|
|
@ -47,7 +47,7 @@ func TestGetSetExtra(t *testing.T) {
|
||||||
// test deep copying.
|
// test deep copying.
|
||||||
payloads := types.RegisterExtras[
|
payloads := types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
*accountExtra,
|
*accountExtra,
|
||||||
]().StateAccount
|
]().StateAccount
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestStateObjectEmpty(t *testing.T) {
|
||||||
registerAndSet: func(acc *types.StateAccount) {
|
registerAndSet: func(acc *types.StateAccount) {
|
||||||
types.RegisterExtras[
|
types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]().StateAccount.Set(acc, false)
|
]().StateAccount.Set(acc, false)
|
||||||
},
|
},
|
||||||
|
|
@ -59,7 +59,7 @@ func TestStateObjectEmpty(t *testing.T) {
|
||||||
registerAndSet: func(*types.StateAccount) {
|
registerAndSet: func(*types.StateAccount) {
|
||||||
types.RegisterExtras[
|
types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
},
|
},
|
||||||
|
|
@ -70,7 +70,7 @@ func TestStateObjectEmpty(t *testing.T) {
|
||||||
registerAndSet: func(acc *types.StateAccount) {
|
registerAndSet: func(acc *types.StateAccount) {
|
||||||
types.RegisterExtras[
|
types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]().StateAccount.Set(acc, true)
|
]().StateAccount.Set(acc, true)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ import (
|
||||||
"github.com/ava-labs/libevm/rlp"
|
"github.com/ava-labs/libevm/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
func newTx(nonce uint64) *Transaction { return NewTx(&LegacyTx{Nonce: nonce}) }
|
||||||
newTx := func(nonce uint64) *Transaction { return NewTx(&LegacyTx{Nonce: nonce}) }
|
func newHdr(parentHashHigh byte) *Header { return &Header{ParentHash: common.Hash{parentHashHigh}} }
|
||||||
newHdr := func(hashLow byte) *Header { return &Header{ParentHash: common.Hash{hashLow}} }
|
func newWithdraw(idx uint64) *Withdrawal { return &Withdrawal{Index: idx} }
|
||||||
newWithdraw := func(idx uint64) *Withdrawal { return &Withdrawal{Index: idx} }
|
|
||||||
|
|
||||||
|
func blockBodyRLPTestInputs() []*Body {
|
||||||
// We build up test-case [Body] instances from the Cartesian product of each
|
// We build up test-case [Body] instances from the Cartesian product of each
|
||||||
// of these components.
|
// of these components.
|
||||||
txMatrix := [][]*Transaction{
|
txMatrix := [][]*Transaction{
|
||||||
|
|
@ -61,8 +61,11 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return bodies
|
||||||
|
}
|
||||||
|
|
||||||
for _, body := range bodies {
|
func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
|
for _, body := range blockBodyRLPTestInputs() {
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
if t.Failed() {
|
if t.Failed() {
|
||||||
|
|
@ -86,8 +89,10 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
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(rlp.EncodeToBytes(%T), %T) resulted in %s",
|
require.NoErrorf(
|
||||||
(*withoutMethods)(body), got, pretty.Sprint(got))
|
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
|
||||||
|
|
@ -112,17 +117,94 @@ func TestBodyRLPBackwardsCompatibility(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockRLPBackwardsCompatibility(t *testing.T) {
|
||||||
|
TestOnlyClearRegisteredExtras()
|
||||||
|
t.Cleanup(TestOnlyClearRegisteredExtras)
|
||||||
|
|
||||||
|
RegisterExtras[
|
||||||
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
NOOPBlockBodyHooks, *NOOPBlockBodyHooks, // types under test
|
||||||
|
struct{},
|
||||||
|
]()
|
||||||
|
|
||||||
|
// Note that there are also a number of tests in `block_test.go` that ensure
|
||||||
|
// backwards compatibility as [NOOPBlockBodyHooks] are used by default when
|
||||||
|
// nothing is registered (the above registration is only for completeness).
|
||||||
|
|
||||||
|
for _, body := range blockBodyRLPTestInputs() {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
// [Block] doesn't export most of its fields so uses [extblock] as a
|
||||||
|
// proxy for RLP encoding, which is what we therefore use as the
|
||||||
|
// backwards-compatible gold standard.
|
||||||
|
hdr := newHdr(99)
|
||||||
|
block := extblock{
|
||||||
|
Header: hdr,
|
||||||
|
Txs: body.Transactions,
|
||||||
|
Uncles: body.Uncles,
|
||||||
|
Withdrawals: body.Withdrawals,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've added [extblock.EncodeRLP] and [extblock.DecodeRLP] for our
|
||||||
|
// hooks.
|
||||||
|
type withoutMethods extblock
|
||||||
|
|
||||||
|
wantRLP, err := rlp.EncodeToBytes(withoutMethods(block))
|
||||||
|
require.NoErrorf(t, err, "rlp.EncodeToBytes([%T with methods stripped])", block)
|
||||||
|
|
||||||
|
// Our input to RLP might not be the canonical RLP output.
|
||||||
|
var wantBlock extblock
|
||||||
|
err = rlp.DecodeBytes(wantRLP, (*withoutMethods)(&wantBlock))
|
||||||
|
require.NoErrorf(t, err, "rlp.DecodeBytes(..., [%T with methods stripped])", &wantBlock)
|
||||||
|
|
||||||
|
t.Run("Encode", func(t *testing.T) {
|
||||||
|
b := NewBlockWithHeader(hdr).WithBody(*body).WithWithdrawals(body.Withdrawals)
|
||||||
|
got, err := rlp.EncodeToBytes(b)
|
||||||
|
require.NoErrorf(t, err, "rlp.EncodeToBytes(%T)", b)
|
||||||
|
|
||||||
|
assert.Equalf(t, wantRLP, got, "expect %T RLP identical to that from %T struct stripped of methods", got, extblock{})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Decode", func(t *testing.T) {
|
||||||
|
var gotBlock Block
|
||||||
|
err := rlp.DecodeBytes(wantRLP, &gotBlock)
|
||||||
|
require.NoErrorf(t, err, "rlp.DecodeBytes(..., %T)", &gotBlock)
|
||||||
|
|
||||||
|
got := extblock{
|
||||||
|
gotBlock.Header(),
|
||||||
|
gotBlock.Transactions(),
|
||||||
|
gotBlock.Uncles(),
|
||||||
|
gotBlock.Withdrawals(),
|
||||||
|
nil, // unexported libevm hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := cmp.Options{
|
||||||
|
cmp.Comparer((*Header).equalHash),
|
||||||
|
cmp.Comparer((*Transaction).equalHash),
|
||||||
|
cmpopts.IgnoreUnexported(extblock{}),
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(wantBlock, got, opts); diff != "" {
|
||||||
|
t.Errorf("rlp.DecodeBytes([RLP from %T stripped of methods], ...) diff (-want +got):\n%s", extblock{}, diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cChainBodyExtras carries the same additional fields as the Avalanche C-Chain
|
// cChainBodyExtras carries the same additional fields as the Avalanche C-Chain
|
||||||
// (ava-labs/coreth) [Body] and implements [BodyHooks] to achieve equivalent RLP
|
// (ava-labs/coreth) [Body] and implements [BlockBodyHooks] to achieve
|
||||||
// {en,de}coding.
|
// equivalent RLP {en,de}coding.
|
||||||
|
//
|
||||||
|
// It is not intended as a full test of ava-labs/coreth existing functionality,
|
||||||
|
// which should be implemented when that module consumes libevm, but as proof of
|
||||||
|
// equivalence of the [rlp.Fields] approach.
|
||||||
type cChainBodyExtras struct {
|
type cChainBodyExtras struct {
|
||||||
Version uint32
|
Version uint32
|
||||||
ExtData *[]byte
|
ExtData *[]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ BodyHooks = (*cChainBodyExtras)(nil)
|
var _ BlockBodyHooks = (*cChainBodyExtras)(nil)
|
||||||
|
|
||||||
func (e *cChainBodyExtras) RLPFieldsForEncoding(b *Body) *rlp.Fields {
|
func (e *cChainBodyExtras) BodyRLPFieldsForEncoding(b *Body) *rlp.Fields {
|
||||||
// The Avalanche C-Chain uses all of the geth required fields (but none of
|
// The Avalanche C-Chain uses all of the geth required fields (but none of
|
||||||
// the optional ones) so there's no need to explicitly list them. This
|
// the optional ones) so there's no need to explicitly list them. This
|
||||||
// pattern might not be ideal for readability but is used here for
|
// pattern might not be ideal for readability but is used here for
|
||||||
|
|
@ -132,13 +214,13 @@ func (e *cChainBodyExtras) RLPFieldsForEncoding(b *Body) *rlp.Fields {
|
||||||
// compatibility so this is safe to do, but only for the required fields.
|
// compatibility so this is safe to do, but only for the required fields.
|
||||||
return &rlp.Fields{
|
return &rlp.Fields{
|
||||||
Required: append(
|
Required: append(
|
||||||
NOOPBodyHooks{}.RLPFieldsForEncoding(b).Required,
|
NOOPBlockBodyHooks{}.BodyRLPFieldsForEncoding(b).Required,
|
||||||
e.Version, e.ExtData,
|
e.Version, e.ExtData,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *cChainBodyExtras) RLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
func (e *cChainBodyExtras) BodyRLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
||||||
// An alternative to the pattern used above is to explicitly list all
|
// An alternative to the pattern used above is to explicitly list all
|
||||||
// fields for better introspection.
|
// fields for better introspection.
|
||||||
return &rlp.Fields{
|
return &rlp.Fields{
|
||||||
|
|
@ -151,6 +233,20 @@ func (e *cChainBodyExtras) RLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See [cChainBodyExtras] intent.
|
||||||
|
|
||||||
|
func (e *cChainBodyExtras) Copy() *cChainBodyExtras {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cChainBodyExtras) BlockRLPFieldsForEncoding(b *BlockRLPProxy) *rlp.Fields {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cChainBodyExtras) BlockRLPFieldPointersForDecoding(b *BlockRLPProxy) *rlp.Fields {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
func TestBodyRLPCChainCompat(t *testing.T) {
|
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]
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func TestHeaderRLPBackwardsCompatibility(t *testing.T) {
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[
|
RegisterExtras[
|
||||||
NOOPHeaderHooks, *NOOPHeaderHooks,
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
NOOPBodyHooks, *NOOPBodyHooks,
|
NOOPBlockBodyHooks, *NOOPBlockBodyHooks,
|
||||||
struct{},
|
struct{},
|
||||||
]()
|
]()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,8 @@ type Block struct {
|
||||||
// inter-peer block relay.
|
// inter-peer block relay.
|
||||||
ReceivedAt time.Time
|
ReceivedAt time.Time
|
||||||
ReceivedFrom interface{}
|
ReceivedFrom interface{}
|
||||||
|
|
||||||
|
extra *pseudo.Type // See [RegisterExtras]
|
||||||
}
|
}
|
||||||
|
|
||||||
// "external" block encoding. used for eth protocol, etc.
|
// "external" block encoding. used for eth protocol, etc.
|
||||||
|
|
@ -219,6 +221,8 @@ type extblock struct {
|
||||||
Txs []*Transaction
|
Txs []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
|
||||||
|
hooks BlockBodyHooks // libevm: MUST be unexported + populated from [Block.hooks]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlock creates a new block. The input data is copied, changes to header and to the
|
// NewBlock creates a new block. The input data is copied, changes to header and to the
|
||||||
|
|
@ -318,6 +322,7 @@ func CopyHeader(h *Header) *Header {
|
||||||
// DecodeRLP decodes a block from RLP.
|
// DecodeRLP decodes a block from RLP.
|
||||||
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
||||||
var eb extblock
|
var eb extblock
|
||||||
|
eb.hooks = b.hooks()
|
||||||
_, size, _ := s.Kind()
|
_, size, _ := s.Kind()
|
||||||
if err := s.Decode(&eb); err != nil {
|
if err := s.Decode(&eb); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -334,13 +339,14 @@ func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
Txs: b.transactions,
|
Txs: b.transactions,
|
||||||
Uncles: b.uncles,
|
Uncles: b.uncles,
|
||||||
Withdrawals: b.withdrawals,
|
Withdrawals: b.withdrawals,
|
||||||
|
hooks: b.hooks(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, nil /* unexported extras field */}
|
return &Body{b.transactions, b.uncles, b.withdrawals, b.cloneExtra()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -458,6 +464,7 @@ func (b *Block) WithSeal(header *Header) *Block {
|
||||||
transactions: b.transactions,
|
transactions: b.transactions,
|
||||||
uncles: b.uncles,
|
uncles: b.uncles,
|
||||||
withdrawals: b.withdrawals,
|
withdrawals: b.withdrawals,
|
||||||
|
extra: b.cloneExtra(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,6 +475,7 @@ func (b *Block) WithBody(body Body) *Block {
|
||||||
transactions: make([]*Transaction, len(body.Transactions)),
|
transactions: make([]*Transaction, len(body.Transactions)),
|
||||||
uncles: make([]*Header, len(body.Uncles)),
|
uncles: make([]*Header, len(body.Uncles)),
|
||||||
withdrawals: b.withdrawals,
|
withdrawals: b.withdrawals,
|
||||||
|
extra: body.cloneExtra(),
|
||||||
}
|
}
|
||||||
copy(block.transactions, body.Transactions)
|
copy(block.transactions, body.Transactions)
|
||||||
for i := range body.Uncles {
|
for i := range body.Uncles {
|
||||||
|
|
@ -482,6 +490,7 @@ func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block {
|
||||||
header: b.header,
|
header: b.header,
|
||||||
transactions: b.transactions,
|
transactions: b.transactions,
|
||||||
uncles: b.uncles,
|
uncles: b.uncles,
|
||||||
|
extra: b.cloneExtra(),
|
||||||
}
|
}
|
||||||
if withdrawals != nil {
|
if withdrawals != nil {
|
||||||
block.withdrawals = make([]*Withdrawal, len(withdrawals))
|
block.withdrawals = make([]*Withdrawal, len(withdrawals))
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/ava-labs/libevm/libevm/pseudo"
|
||||||
"github.com/ava-labs/libevm/rlp"
|
"github.com/ava-labs/libevm/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -84,46 +85,95 @@ func (*NOOPHeaderHooks) DecodeRLP(h *Header, s *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
func (*NOOPHeaderHooks) PostCopy(dst *Header) {}
|
func (*NOOPHeaderHooks) PostCopy(dst *Header) {}
|
||||||
|
|
||||||
var _ interface {
|
var _ = []interface {
|
||||||
rlp.Encoder
|
rlp.Encoder
|
||||||
rlp.Decoder
|
rlp.Decoder
|
||||||
} = (*Body)(nil)
|
}{
|
||||||
|
(*Body)(nil),
|
||||||
|
(*extblock)(nil),
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeRLP implements the [rlp.Encoder] interface.
|
// EncodeRLP implements the [rlp.Encoder] interface.
|
||||||
func (b *Body) EncodeRLP(w io.Writer) error {
|
func (b *Body) EncodeRLP(w io.Writer) error {
|
||||||
return b.hooks().RLPFieldsForEncoding(b).EncodeRLP(w)
|
return b.hooks().BodyRLPFieldsForEncoding(b).EncodeRLP(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeRLP implements the [rlp.Decoder] interface.
|
// DecodeRLP implements the [rlp.Decoder] interface.
|
||||||
func (b *Body) DecodeRLP(s *rlp.Stream) error {
|
func (b *Body) DecodeRLP(s *rlp.Stream) error {
|
||||||
return b.hooks().RLPFieldPointersForDecoding(b).DecodeRLP(s)
|
return b.hooks().BodyRLPFieldPointersForDecoding(b).DecodeRLP(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BodyHooks are required for all types registered with [RegisterExtras] for
|
// BlockRLPProxy exports the geth-internal type used for RLP {en,de}coding of a
|
||||||
// [Body] payloads.
|
// [Block].
|
||||||
type BodyHooks interface {
|
type BlockRLPProxy extblock
|
||||||
RLPFieldsForEncoding(*Body) *rlp.Fields
|
|
||||||
RLPFieldPointersForDecoding(*Body) *rlp.Fields
|
func (b *extblock) EncodeRLP(w io.Writer) error {
|
||||||
|
bb := (*BlockRLPProxy)(b)
|
||||||
|
return b.hooks.BlockRLPFieldsForEncoding(bb).EncodeRLP(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOOPBodyHooks implements [BodyHooks] such that they are equivalent to no type
|
func (b *extblock) DecodeRLP(s *rlp.Stream) error {
|
||||||
// having been registered.
|
bb := (*BlockRLPProxy)(b)
|
||||||
type NOOPBodyHooks struct{}
|
return b.hooks.BlockRLPFieldPointersForDecoding(bb).DecodeRLP(s)
|
||||||
|
}
|
||||||
|
|
||||||
// The RLP-related methods of [NOOPBodyHooks] make assumptions about the struct
|
// BlockBodyHooks are required for all types registered with [RegisterExtras]
|
||||||
// fields and their order, which we lock in here as a change detector. If this
|
// for [Block] and [Body] payloads.
|
||||||
// breaks then it MUST be updated and the RLP methods reviewed + new
|
type BlockBodyHooks interface {
|
||||||
|
BlockRLPFieldsForEncoding(*BlockRLPProxy) *rlp.Fields
|
||||||
|
BlockRLPFieldPointersForDecoding(*BlockRLPProxy) *rlp.Fields
|
||||||
|
BodyRLPFieldsForEncoding(*Body) *rlp.Fields
|
||||||
|
BodyRLPFieldPointersForDecoding(*Body) *rlp.Fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOOPBlockBodyHooks implements [BlockBodyHooks] such that they are equivalent
|
||||||
|
// to no type having been registered.
|
||||||
|
type NOOPBlockBodyHooks struct{}
|
||||||
|
|
||||||
|
var _ BlockBodyPayload[*NOOPBlockBodyHooks] = NOOPBlockBodyHooks{}
|
||||||
|
|
||||||
|
func (NOOPBlockBodyHooks) Copy() *NOOPBlockBodyHooks { return &NOOPBlockBodyHooks{} }
|
||||||
|
|
||||||
|
// The RLP-related methods of [NOOPBlockBodyHooks] make assumptions about the
|
||||||
|
// struct fields and their order, which we lock in here as a change detector. If
|
||||||
|
// these break then they MUST be updated and the RLP methods reviewed + new
|
||||||
// backwards-compatibility tests added.
|
// backwards-compatibility tests added.
|
||||||
var _ = &Body{[]*Transaction{}, []*Header{}, []*Withdrawal{}, nil /* extra unexported type */}
|
var (
|
||||||
|
_ = &Body{
|
||||||
|
[]*Transaction{}, []*Header{}, []*Withdrawal{}, // geth
|
||||||
|
&pseudo.Type{}, // libevm
|
||||||
|
}
|
||||||
|
_ = extblock{
|
||||||
|
&Header{}, []*Transaction{}, []*Header{}, []*Withdrawal{}, // geth
|
||||||
|
BlockBodyHooks(nil), // libevm
|
||||||
|
}
|
||||||
|
// Demonstrate identity of these two types, by definition but useful for
|
||||||
|
// inspection here.
|
||||||
|
_ = extblock(BlockRLPProxy{})
|
||||||
|
)
|
||||||
|
|
||||||
func (NOOPBodyHooks) RLPFieldsForEncoding(b *Body) *rlp.Fields {
|
func (NOOPBlockBodyHooks) BlockRLPFieldsForEncoding(b *BlockRLPProxy) *rlp.Fields {
|
||||||
|
return &rlp.Fields{
|
||||||
|
Required: []any{b.Header, b.Txs, b.Uncles},
|
||||||
|
Optional: []any{b.Withdrawals},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NOOPBlockBodyHooks) BlockRLPFieldPointersForDecoding(b *BlockRLPProxy) *rlp.Fields {
|
||||||
|
return &rlp.Fields{
|
||||||
|
Required: []any{&b.Header, &b.Txs, &b.Uncles},
|
||||||
|
Optional: []any{&b.Withdrawals},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NOOPBlockBodyHooks) BodyRLPFieldsForEncoding(b *Body) *rlp.Fields {
|
||||||
return &rlp.Fields{
|
return &rlp.Fields{
|
||||||
Required: []any{b.Transactions, b.Uncles},
|
Required: []any{b.Transactions, b.Uncles},
|
||||||
Optional: []any{b.Withdrawals},
|
Optional: []any{b.Withdrawals},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NOOPBodyHooks) RLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
func (NOOPBlockBodyHooks) BodyRLPFieldPointersForDecoding(b *Body) *rlp.Fields {
|
||||||
return &rlp.Fields{
|
return &rlp.Fields{
|
||||||
Required: []any{&b.Transactions, &b.Uncles},
|
Required: []any{&b.Transactions, &b.Uncles},
|
||||||
Optional: []any{&b.Withdrawals},
|
Optional: []any{&b.Withdrawals},
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -88,7 +90,7 @@ func TestHeaderHooks(t *testing.T) {
|
||||||
|
|
||||||
extras := RegisterExtras[
|
extras := RegisterExtras[
|
||||||
stubHeaderHooks, *stubHeaderHooks,
|
stubHeaderHooks, *stubHeaderHooks,
|
||||||
NOOPBodyHooks, *NOOPBodyHooks,
|
NOOPBlockBodyHooks, *NOOPBlockBodyHooks,
|
||||||
struct{},
|
struct{},
|
||||||
]()
|
]()
|
||||||
rng := ethtest.NewPseudoRand(13579)
|
rng := ethtest.NewPseudoRand(13579)
|
||||||
|
|
@ -200,3 +202,71 @@ func TestHeaderHooks(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blockPayload struct {
|
||||||
|
NOOPBlockBodyHooks
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *blockPayload) Copy() *blockPayload {
|
||||||
|
return &blockPayload{x: p.x}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockWithX(t *testing.T) {
|
||||||
|
TestOnlyClearRegisteredExtras()
|
||||||
|
t.Cleanup(TestOnlyClearRegisteredExtras)
|
||||||
|
|
||||||
|
extras := RegisterExtras[
|
||||||
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
|
blockPayload, *blockPayload,
|
||||||
|
struct{},
|
||||||
|
]()
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(&Block{})
|
||||||
|
for i := 0; i < typ.NumMethod(); i++ {
|
||||||
|
method := typ.Method(i).Name
|
||||||
|
if method == "Withdrawals" || !strings.HasPrefix(method, "With") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
block := NewBlockWithHeader(&Header{})
|
||||||
|
const initialPayload = int(42)
|
||||||
|
payload := &blockPayload{
|
||||||
|
x: initialPayload,
|
||||||
|
}
|
||||||
|
extras.Block.Set(block, payload)
|
||||||
|
|
||||||
|
t.Run(method, func(t *testing.T) {
|
||||||
|
var newBlock *Block
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "WithBody":
|
||||||
|
var body Body
|
||||||
|
extras.Body.Set(&body, payload)
|
||||||
|
newBlock = block.WithBody(body)
|
||||||
|
case "WithSeal":
|
||||||
|
newBlock = block.WithSeal(&Header{})
|
||||||
|
case "WithWithdrawals":
|
||||||
|
newBlock = block.WithWithdrawals(nil)
|
||||||
|
default:
|
||||||
|
t.Fatalf("method call not implemented: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.x++
|
||||||
|
// This specifically uses `require` instead of `assert` because a
|
||||||
|
// failure here invalidates the next test, which demonstrates a deep
|
||||||
|
// copy.
|
||||||
|
require.Equalf(t, initialPayload+1, extras.Block.Get(block).x, "%T payload %T after modification via pointer", block, payload)
|
||||||
|
|
||||||
|
switch got := extras.Block.Get(newBlock); got.x {
|
||||||
|
case initialPayload: // expected
|
||||||
|
case 0:
|
||||||
|
t.Errorf("%T payload %T got zero value; the payload was probably not copied, resulting in a default being created", newBlock, got)
|
||||||
|
case initialPayload + 1:
|
||||||
|
t.Errorf("%T payload %T got same value as modified original; the payload was probably shallow copied", newBlock, got)
|
||||||
|
default:
|
||||||
|
t.Errorf("%T payload %T got %d, want %d; this is unexpected even as an error so you're on your own here", newBlock, got, got.x, initialPayload)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterExtras registers the type `HPtr` to be carried as an extra payload in
|
// RegisterExtras registers the type `HPtr` to be carried as an extra payload in
|
||||||
// [Header] structs and the type `SA` in [StateAccount] and [SlimAccount]
|
// [Header] structs, the type `BPtr` in [Block] and [Body] structs, and the type
|
||||||
// structs. It is expected to be called in an `init()` function and MUST NOT be
|
// `SA` in [StateAccount] and [SlimAccount] structs. It is expected to be called
|
||||||
// called more than once.
|
// in an `init()` function and MUST NOT be called more than once.
|
||||||
//
|
//
|
||||||
// The `SA` payload will be treated as an extra struct field for the purposes of
|
// The `SA` payload will be treated as an extra struct field for the purposes of
|
||||||
// RLP encoding and decoding. RLP handling is plumbed through to the `SA` via
|
// RLP encoding and decoding. RLP handling is plumbed through to the `SA` via
|
||||||
|
|
@ -39,15 +39,15 @@ import (
|
||||||
// The payloads can be accessed via the [pseudo.Accessor] methods of the
|
// The payloads can be accessed via the [pseudo.Accessor] methods of the
|
||||||
// [ExtraPayloads] returned by RegisterExtras. The default `SA` value accessed
|
// [ExtraPayloads] returned by RegisterExtras. The default `SA` value accessed
|
||||||
// in this manner will be a zero-value `SA` while the default value from a
|
// in this manner will be a zero-value `SA` while the default value from a
|
||||||
// [Header] is a non-nil `HPtr`. The latter guarantee ensures that hooks won't
|
// [Header] or [Block] / [Body] is a non-nil `HPtr` or `BPtr` respectively. The
|
||||||
// be called on nil-pointer receivers.
|
// latter guarantee ensures that hooks won't be called on nil-pointer receivers.
|
||||||
func RegisterExtras[
|
func RegisterExtras[
|
||||||
H any, HPtr interface {
|
H any, HPtr interface {
|
||||||
HeaderHooks
|
HeaderHooks
|
||||||
*H
|
*H
|
||||||
},
|
},
|
||||||
B any, BPtr interface {
|
B any, BPtr interface {
|
||||||
BodyHooks
|
BlockBodyPayload[BPtr]
|
||||||
*B
|
*B
|
||||||
},
|
},
|
||||||
SA any,
|
SA any,
|
||||||
|
|
@ -61,6 +61,10 @@ func RegisterExtras[
|
||||||
(*Body).extraPayload,
|
(*Body).extraPayload,
|
||||||
func(b *Body, t *pseudo.Type) { b.extra = t },
|
func(b *Body, t *pseudo.Type) { b.extra = t },
|
||||||
),
|
),
|
||||||
|
Block: pseudo.NewAccessor[*Block, BPtr](
|
||||||
|
(*Block).extraPayload,
|
||||||
|
func(b *Block, 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 },
|
||||||
|
|
@ -72,16 +76,25 @@ func RegisterExtras[
|
||||||
return fmt.Sprintf("%T", x)
|
return fmt.Sprintf("%T", x)
|
||||||
}(),
|
}(),
|
||||||
// The [ExtraPayloads] that we returns is based on [HPtr,BPtr,SA], not
|
// The [ExtraPayloads] that we returns is based on [HPtr,BPtr,SA], not
|
||||||
// [H,B,SA] so our constructors MUST match that. This guarantees that calls to
|
// [H,B,SA] so our constructors MUST match that. This guarantees that
|
||||||
// the [HeaderHooks] and [BodyHooks] methods will never be performed on a nil pointer.
|
// calls to the [HeaderHooks] and [BlockBodyHooks] 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
|
newBlockOrBody: pseudo.NewConstructor[B]().NewPointer, // i.e. non-nil BPtr
|
||||||
newStateAccount: pseudo.NewConstructor[SA]().Zero,
|
newStateAccount: pseudo.NewConstructor[SA]().Zero,
|
||||||
hooks: extra,
|
hooks: extra,
|
||||||
})
|
})
|
||||||
return extra
|
return extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A BlockBodyPayload is an implementation of [BlockBodyHooks] that is also able
|
||||||
|
// to clone itself. Both [Block.Body] and [Block.WithBody] require this
|
||||||
|
// functionality to copy the payload between the types.
|
||||||
|
type BlockBodyPayload[BPtr any] interface {
|
||||||
|
BlockBodyHooks
|
||||||
|
Copy() BPtr
|
||||||
|
}
|
||||||
|
|
||||||
// TestOnlyClearRegisteredExtras clears the [Extras] previously passed to
|
// TestOnlyClearRegisteredExtras clears the [Extras] previously passed to
|
||||||
// [RegisterExtras]. It panics if called from a non-testing call stack.
|
// [RegisterExtras]. It panics if called from a non-testing call stack.
|
||||||
//
|
//
|
||||||
|
|
@ -97,11 +110,14 @@ var registeredExtras register.AtMostOnce[*extraConstructors]
|
||||||
type extraConstructors struct {
|
type extraConstructors struct {
|
||||||
stateAccountType string
|
stateAccountType string
|
||||||
newHeader func() *pseudo.Type
|
newHeader func() *pseudo.Type
|
||||||
newBody func() *pseudo.Type
|
newBlockOrBody func() *pseudo.Type
|
||||||
newStateAccount func() *pseudo.Type
|
newStateAccount func() *pseudo.Type
|
||||||
hooks interface {
|
hooks interface {
|
||||||
hooksFromHeader(*Header) HeaderHooks
|
hooksFromHeader(*Header) HeaderHooks
|
||||||
hooksFromBody(*Body) BodyHooks
|
hooksFromBody(*Body) BlockBodyHooks
|
||||||
|
hooksFromBlock(*Block) BlockBodyHooks
|
||||||
|
cloneBlockPayload(*Block) *pseudo.Type
|
||||||
|
cloneBodyPayload(*Body) *pseudo.Type
|
||||||
cloneStateAccount(*StateAccountExtra) *StateAccountExtra
|
cloneStateAccount(*StateAccountExtra) *StateAccountExtra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,12 +142,16 @@ func (h *Header) extraPayload() *pseudo.Type {
|
||||||
|
|
||||||
func (b *Body) extraPayload() *pseudo.Type {
|
func (b *Body) extraPayload() *pseudo.Type {
|
||||||
return extraPayloadOrSetDefault(&b.extra, func(c *extraConstructors) *pseudo.Type {
|
return extraPayloadOrSetDefault(&b.extra, func(c *extraConstructors) *pseudo.Type {
|
||||||
return c.newBody()
|
return c.newBlockOrBody()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Block) extraPayload() *pseudo.Type {
|
||||||
|
return extraPayloadOrSetDefault(&b.extra, func(c *extraConstructors) *pseudo.Type {
|
||||||
|
return c.newBlockOrBody()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// hooks returns the [Header]'s registered [HeaderHooks], if any, otherwise a
|
|
||||||
// [NOOPHeaderHooks] suitable for running default behaviour.
|
|
||||||
func (h *Header) hooks() HeaderHooks {
|
func (h *Header) hooks() HeaderHooks {
|
||||||
if r := registeredExtras; r.Registered() {
|
if r := registeredExtras; r.Registered() {
|
||||||
return r.Get().hooks.hooksFromHeader(h)
|
return r.Get().hooks.hooksFromHeader(h)
|
||||||
|
|
@ -139,13 +159,18 @@ func (h *Header) hooks() HeaderHooks {
|
||||||
return new(NOOPHeaderHooks)
|
return new(NOOPHeaderHooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hooks returns the [Body]'s registered [BodyHooks], if any, otherwise a
|
func (b *Body) hooks() BlockBodyHooks {
|
||||||
// [NOOPBodyHooks] suitable for running default behaviour.
|
|
||||||
func (b *Body) hooks() BodyHooks {
|
|
||||||
if r := registeredExtras; r.Registered() {
|
if r := registeredExtras; r.Registered() {
|
||||||
return r.Get().hooks.hooksFromBody(b)
|
return r.Get().hooks.hooksFromBody(b)
|
||||||
}
|
}
|
||||||
return NOOPBodyHooks{}
|
return NOOPBlockBodyHooks{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Block) hooks() BlockBodyHooks {
|
||||||
|
if r := registeredExtras; r.Registered() {
|
||||||
|
return r.Get().hooks.hooksFromBlock(b)
|
||||||
|
}
|
||||||
|
return NOOPBlockBodyHooks{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StateAccountExtra) clone() *StateAccountExtra {
|
func (e *StateAccountExtra) clone() *StateAccountExtra {
|
||||||
|
|
@ -160,14 +185,16 @@ 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], [Body], [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, BPtr BodyHooks, SA any] struct {
|
type ExtraPayloads[HPtr HeaderHooks, BPtr BlockBodyPayload[BPtr], SA any] struct {
|
||||||
Header pseudo.Accessor[*Header, HPtr]
|
Header pseudo.Accessor[*Header, HPtr]
|
||||||
|
Block pseudo.Accessor[*Block, BPtr]
|
||||||
Body pseudo.Accessor[*Body, BPtr]
|
Body pseudo.Accessor[*Body, BPtr]
|
||||||
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
|
StateAccount pseudo.Accessor[StateOrSlimAccount, SA] // Also provides [SlimAccount] access.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromHeader(h *Header) HeaderHooks { return e.Header.Get(h) }
|
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromHeader(h *Header) HeaderHooks { return e.Header.Get(h) }
|
||||||
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromBody(b *Body) BodyHooks { return e.Body.Get(b) }
|
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromBody(b *Body) BlockBodyHooks { return e.Body.Get(b) }
|
||||||
|
func (e ExtraPayloads[HPtr, BPtr, SA]) hooksFromBlock(b *Block) BlockBodyHooks { return e.Block.Get(b) }
|
||||||
|
|
||||||
func (ExtraPayloads[HPtr, BPtr, 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)
|
||||||
|
|
@ -176,6 +203,43 @@ func (ExtraPayloads[HPtr, BPtr, SA]) cloneStateAccount(s *StateAccountExtra) *St
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// blockOrBody is an interface for use as a method argument as they can't
|
||||||
|
// introduce new generic type parameters.
|
||||||
|
type blockOrBody interface {
|
||||||
|
isBlockOrBody() // noop to restrict type as [Header.extraPayload] otherwise matches
|
||||||
|
extraPayload() *pseudo.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Block) isBlockOrBody() {}
|
||||||
|
func (*Body) isBlockOrBody() {}
|
||||||
|
|
||||||
|
func (e ExtraPayloads[HPtr, BPtr, SA]) cloneBodyPayload(b *Body) *pseudo.Type {
|
||||||
|
return e.cloneBlockOrBodyPayload(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ExtraPayloads[HPtr, BPtr, SA]) cloneBlockPayload(b *Block) *pseudo.Type {
|
||||||
|
return e.cloneBlockOrBodyPayload(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ExtraPayloads[HPtr, BPtr, SA]) cloneBlockOrBodyPayload(b blockOrBody) *pseudo.Type {
|
||||||
|
v := pseudo.MustNewValue[BPtr](b.extraPayload())
|
||||||
|
return pseudo.From(v.Get().Copy()).Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Body) cloneExtra() *pseudo.Type {
|
||||||
|
if r := registeredExtras; r.Registered() {
|
||||||
|
return r.Get().hooks.cloneBodyPayload(b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Block) cloneExtra() *pseudo.Type {
|
||||||
|
if r := registeredExtras; r.Registered() {
|
||||||
|
return r.Get().hooks.cloneBlockPayload(b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// StateOrSlimAccount is implemented by both [StateAccount] and [SlimAccount],
|
// StateOrSlimAccount is implemented by both [StateAccount] and [SlimAccount],
|
||||||
// allowing for their [StateAccountExtra] payloads to be accessed in a type-safe
|
// allowing for their [StateAccountExtra] payloads to be accessed in a type-safe
|
||||||
// manner by [ExtraPayloads] instances.
|
// manner by [ExtraPayloads] instances.
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestStateAccountRLP(t *testing.T) {
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[
|
RegisterExtras[
|
||||||
NOOPHeaderHooks, *NOOPHeaderHooks,
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
NOOPBodyHooks, *NOOPBodyHooks,
|
NOOPBlockBodyHooks, *NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
},
|
},
|
||||||
|
|
@ -82,7 +82,7 @@ func TestStateAccountRLP(t *testing.T) {
|
||||||
register: func() {
|
register: func() {
|
||||||
RegisterExtras[
|
RegisterExtras[
|
||||||
NOOPHeaderHooks, *NOOPHeaderHooks,
|
NOOPHeaderHooks, *NOOPHeaderHooks,
|
||||||
NOOPBodyHooks, *NOOPBodyHooks,
|
NOOPBlockBodyHooks, *NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[
|
e := types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
e.StateAccount.Set(a, true)
|
e.StateAccount.Set(a, true)
|
||||||
|
|
@ -90,7 +90,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[
|
e := types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
e.StateAccount.Set(a, false) // the explicit part
|
e.StateAccount.Set(a, false) // the explicit part
|
||||||
|
|
@ -106,7 +106,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[
|
e := types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
bool,
|
bool,
|
||||||
]()
|
]()
|
||||||
// Note that `a` is reflected, unchanged (the implicit part).
|
// Note that `a` is reflected, unchanged (the implicit part).
|
||||||
|
|
@ -121,7 +121,7 @@ func TestStateAccountExtraViaTrieStorage(t *testing.T) {
|
||||||
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
registerAndSetExtra: func(a *types.StateAccount) (*types.StateAccount, assertion) {
|
||||||
e := types.RegisterExtras[
|
e := types.RegisterExtras[
|
||||||
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
types.NOOPHeaderHooks, *types.NOOPHeaderHooks,
|
||||||
types.NOOPBodyHooks, *types.NOOPBodyHooks,
|
types.NOOPBlockBodyHooks, *types.NOOPBlockBodyHooks,
|
||||||
arbitraryPayload,
|
arbitraryPayload,
|
||||||
]()
|
]()
|
||||||
p := arbitraryPayload{arbitraryData}
|
p := arbitraryPayload{arbitraryData}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue