Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions core/types/backwards_compat.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,14 +240,6 @@ 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) {
// 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]
Expand Down
6 changes: 3 additions & 3 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ type extblock struct {
Uncles []*Header
Withdrawals []*Withdrawal `rlp:"optional"`

hooks BlockBodyHooks // libevm: MUST be unexported + populated from [Block.hooks]
extra *pseudo.Type // libevm: MUST be unexported + populated from [Block.extraOrNil]
}

// NewBlock creates a new block. The input data is copied, changes to header and to the
Expand Down Expand Up @@ -322,7 +322,7 @@ func CopyHeader(h *Header) *Header {
// DecodeRLP decodes a block from RLP.
func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock
eb.hooks = b.hooks()
eb.extra = b.extraOrNil()
_, size, _ := s.Kind()
if err := s.Decode(&eb); err != nil {
return err
Expand All @@ -339,7 +339,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
Txs: b.transactions,
Uncles: b.uncles,
Withdrawals: b.withdrawals,
hooks: b.hooks(),
extra: b.extraOrNil(),
})
}

Expand Down
91 changes: 62 additions & 29 deletions core/types/block.libevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package types

import (
"encoding/json"
"fmt"
"io"

"github.com/ava-labs/libevm/internal/libevm/pseudo"
Expand Down Expand Up @@ -106,25 +107,74 @@ func (b *Body) DecodeRLP(s *rlp.Stream) error {
return b.hooks().BodyRLPFieldPointersForDecoding(b).DecodeRLP(s)
}

// BlockRLPProxy exports the geth-internal type used for RLP {en,de}coding of a
// [Block].
type BlockRLPProxy extblock

func (b *extblock) EncodeRLP(w io.Writer) error {
bb := (*BlockRLPProxy)(b)
return b.hooks.BlockRLPFieldsForEncoding(bb).EncodeRLP(w)
body := Body{
Transactions: b.Txs,
Uncles: b.Uncles,
Withdrawals: b.Withdrawals,
extra: b.extra,
}
fields := body.hooks().BodyRLPFieldsForEncoding(&body)
fields.Required = append(
[]any{b.Header},
fields.Required...,
)
return fields.EncodeRLP(w)
}

func (b *extblock) DecodeRLP(s *rlp.Stream) error {
bb := (*BlockRLPProxy)(b)
return b.hooks.BlockRLPFieldPointersForDecoding(bb).DecodeRLP(s)
body := Body{
// The body provided to the hooks is expected to contain the same extra
// as the method receiver.
extra: b.extra,
Comment on lines +130 to +132

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was really the annoying part. It's kind of weird, since I don't think an implementation should actually use this... But I think we should guarantee that it is provided correctly. Thoughts on simplifying this @ARR4N?

}
fields := body.hooks().BodyRLPFieldPointersForDecoding(&body)
fields.Required = append(
[]any{&b.Header},
fields.Required...,
)
if err := fields.DecodeRLP(s); err != nil {
return err
}
b.Txs = body.Transactions
b.Uncles = body.Uncles
b.Withdrawals = body.Withdrawals
return nil
}

// BlockBytes combines an RLP encoded [Header] and [Body] into an RLP encoded
// [Block].
//
// For correctly formatted inputs it is a faster equivalent of:
// - Decoding into a [Header] and [Body]
// - Combining them into a Block
// - Encoding the Block
//
// This function does NOT validate the header or body.
func BlockBytes(headerBytes, bodyBytes []byte) ([]byte, error) {
bodyFields, _, err := rlp.SplitList(bodyBytes)
if err != nil {
return nil, fmt.Errorf("splitting body: %w", err)
}

Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
w := rlp.NewEncoderBuffer(nil)
l := w.List()
if _, err := w.Write(headerBytes); err != nil {
return nil, fmt.Errorf("writing header: %w", err)
}
if _, err := w.Write(bodyFields); err != nil {
return nil, fmt.Errorf("writing body: %w", err)
}
w.ListEnd(l)
blockBytes := w.ToBytes()
return blockBytes, w.Flush() // Flush returns the internal buffer to the pool.
Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
}

// BlockBodyHooks are required for all types registered with [RegisterExtras]
// for [Block] and [Body] payloads.
// for [Block] and [Body] payloads. The same methods are used for both [Block]
// and [Body] {en,de}coding as a Block is encoded as its [Header] followed by
// the fields of its [Body].
type BlockBodyHooks interface {
BlockRLPFieldsForEncoding(*BlockRLPProxy) *rlp.Fields
BlockRLPFieldPointersForDecoding(*BlockRLPProxy) *rlp.Fields
BodyRLPFieldsForEncoding(*Body) *rlp.Fields
BodyRLPFieldPointersForDecoding(*Body) *rlp.Fields
Comment thread
StephenButtolph marked this conversation as resolved.
PostRPCMarshal(b *Block, marshalled map[string]any)
Expand All @@ -149,27 +199,10 @@ var (
}
_ = extblock{
&Header{}, []*Transaction{}, []*Header{}, []*Withdrawal{}, // geth
BlockBodyHooks(nil), // libevm
&pseudo.Type{}, // libevm
}
// Demonstrate identity of these two types, by definition but useful for
// inspection here.
_ = extblock(BlockRLPProxy{})
)

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{
Required: []any{b.Transactions, b.Uncles},
Expand Down
Loading