Skip to content

Commit

Permalink
Use generated RLP encoding/decoding for Header instead of reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
Kourin1996 committed Dec 2, 2024
1 parent 710ed3d commit 62dc9b2
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 12 deletions.
1 change: 0 additions & 1 deletion core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
}

//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
//go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go

// Header represents a block header in the Ethereum blockchain.
type Header struct {
Expand Down
27 changes: 16 additions & 11 deletions core/types/celo_block.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package types

import (
"bytes"
"io"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)

//go:generate go run ../../rlp/rlpgen -type BeforeGingerbreadHeader --encoder --decoder -out gen_before_gingerbread_header_rlp.go
//go:generate go run ../../rlp/rlpgen -type AfterGingerbreadHeader --encoder --decoder -out gen_after_gingerbread_header_rlp.go

type IstanbulExtra rlp.RawValue

type beforeGingerbreadHeader struct {
type BeforeGingerbreadHeader struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
Expand All @@ -24,7 +28,7 @@ type beforeGingerbreadHeader struct {
}

// This type is required to avoid an infinite loop when decoding
type afterGingerbreadHeader Header
type AfterGingerbreadHeader Header

func (h *Header) DecodeRLP(s *rlp.Stream) error {
var raw rlp.RawValue
Expand All @@ -40,8 +44,9 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {

if preGingerbread { // Address
// Before gingerbread
decodedHeader := beforeGingerbreadHeader{}
err = rlp.DecodeBytes(raw, &decodedHeader)
decodedHeader := BeforeGingerbreadHeader{}
str := rlp.NewStream(bytes.NewReader(raw), uint64(len(raw)))
err = decodedHeader.DecodeRLP(str)

h.ParentHash = decodedHeader.ParentHash
h.Coinbase = decodedHeader.Coinbase
Expand All @@ -56,8 +61,9 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {
h.Difficulty = new(big.Int)
} else {
// After gingerbread
decodedHeader := afterGingerbreadHeader{}
err = rlp.DecodeBytes(raw, &decodedHeader)
decodedHeader := AfterGingerbreadHeader{}
str := rlp.NewStream(bytes.NewReader(raw), uint64(len(raw)))
err = decodedHeader.DecodeRLP(str)

h.ParentHash = decodedHeader.ParentHash
h.UncleHash = decodedHeader.UncleHash
Expand Down Expand Up @@ -87,8 +93,7 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {
// EncodeRLP implements encodes the Header to an RLP data stream.
func (h *Header) EncodeRLP(w io.Writer) error {
if h.IsPreGingerbread() {
// Encode the header
encodedHeader := beforeGingerbreadHeader{
encodedHeader := BeforeGingerbreadHeader{
ParentHash: h.ParentHash,
Coinbase: h.Coinbase,
Root: h.Root,
Expand All @@ -101,11 +106,11 @@ func (h *Header) EncodeRLP(w io.Writer) error {
Extra: h.Extra,
}

return rlp.Encode(w, &encodedHeader)
return encodedHeader.EncodeRLP(w)
}

// After gingerbread
encodedHeader := afterGingerbreadHeader{
encodedHeader := AfterGingerbreadHeader{
ParentHash: h.ParentHash,
UncleHash: h.UncleHash,
Coinbase: h.Coinbase,
Expand All @@ -128,7 +133,7 @@ func (h *Header) EncodeRLP(w io.Writer) error {
ParentBeaconRoot: h.ParentBeaconRoot,
}

return rlp.Encode(w, &encodedHeader)
return encodedHeader.EncodeRLP(w)
}

// isPreGingerbreadHeader introspects the header rlp to check the length of the
Expand Down
151 changes: 151 additions & 0 deletions core/types/celo_block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package types

import (
"bytes"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/assert"
)

var (
mockBeforeGingerbreadHeader = &BeforeGingerbreadHeader{
ParentHash: common.HexToHash("0x112233445566778899001122334455667788990011223344556677889900aabb"),
Coinbase: common.HexToAddress("0x8888f1f195afa192cfee860698584c030f4c9db1"),
Root: EmptyRootHash,
TxHash: EmptyTxsHash,
ReceiptHash: EmptyReceiptsHash,
Bloom: Bloom(common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
Number: math.BigPow(2, 9),
GasUsed: 1476322,
Time: 9876543,
Extra: []byte("test before gingerbread header extra"),
}

mockWithdrawalHash = common.HexToHash("0x4585754a71d14791295bc094dc53eb0b32f21d92e58350a4140163a047b854a7")
mockExcessBlobGas = uint64(123456789)
mockBlobGasUsed = uint64(12345678)
mockParentBeaconRoot = common.HexToHash("0x9229c626ebd6328b3ddc7fe8636f2fd9a344f4c02e2e281f59a3b7e4e46833e5")
mockAfterGingerbreadHeader = &AfterGingerbreadHeader{
ParentHash: mockBeforeGingerbreadHeader.ParentHash,
UncleHash: EmptyUncleHash,
Coinbase: mockBeforeGingerbreadHeader.Coinbase,
Root: EmptyRootHash,
TxHash: EmptyTxsHash,
ReceiptHash: EmptyReceiptsHash,
Bloom: mockBeforeGingerbreadHeader.Bloom,
Difficulty: big.NewInt(17179869184),
Number: mockBeforeGingerbreadHeader.Number,
GasLimit: 12345678,
GasUsed: mockBeforeGingerbreadHeader.GasUsed,
Time: mockBeforeGingerbreadHeader.Time,
Extra: []byte("test after gingerbread header extra"),
MixDigest: common.HexToHash("0x036a0a7a3611ecd974ef274e603ceab81246fb50dc350519b9f47589e8fe3014"),
Nonce: EncodeNonce(12345),
BaseFee: math.BigPow(10, 8),
WithdrawalsHash: &mockWithdrawalHash,
ExcessBlobGas: &mockExcessBlobGas,
BlobGasUsed: &mockBlobGasUsed,
ParentBeaconRoot: &mockParentBeaconRoot,
}
)

func BeforeGingerbreadHeaderToHeader(h *BeforeGingerbreadHeader) *Header {
return &Header{
ParentHash: h.ParentHash,
Coinbase: h.Coinbase,
Root: h.Root,
TxHash: h.TxHash,
ReceiptHash: h.ReceiptHash,
Bloom: h.Bloom,
Number: h.Number,
GasUsed: h.GasUsed,
Time: h.Time,
Extra: h.Extra,
Difficulty: new(big.Int),
}
}

func TestRLPDecodeHeaderCompatibility(t *testing.T) {
tests := []struct {
name string
subTypeHeader interface{}
header *Header
}{
{
name: "BeforeGingerbreadHeader",
subTypeHeader: mockBeforeGingerbreadHeader,
header: BeforeGingerbreadHeaderToHeader(mockBeforeGingerbreadHeader),
},
{
name: "AfterGingerbreadHeader",
subTypeHeader: mockAfterGingerbreadHeader,
header: (*Header)(mockAfterGingerbreadHeader),
},
}

for _, test := range tests {
test := test

t.Run(test.name, func(t *testing.T) {
t.Parallel()

r := bytes.NewBuffer([]byte{})

err := rlp.Encode(r, test.subTypeHeader)
assert.NoError(t, err, "failed rlp.Encode for sub type Header")

decodedHeader := &Header{}
err = decodedHeader.DecodeRLP(rlp.NewStream(r, uint64(r.Len())))
assert.NoError(t, err, "failed DecodeRLP of Header")

assert.Equal(t, test.header, decodedHeader)
})
}
}

func TestRlpEncodeHeaderCompatibility(t *testing.T) {
tests := []struct {
name string
subTypeHeader interface{}
header *Header
}{
{
name: "BeforeGingerbreadHeader",
subTypeHeader: mockBeforeGingerbreadHeader,
header: BeforeGingerbreadHeaderToHeader(mockBeforeGingerbreadHeader),
},
{
name: "AfterGingerbreadHeader",
subTypeHeader: mockAfterGingerbreadHeader,
header: (*Header)(mockAfterGingerbreadHeader),
},
}

for _, test := range tests {
test := test

t.Run(test.name, func(t *testing.T) {
t.Parallel()

r := bytes.NewBuffer([]byte{})

// old RLP encoding
err := rlp.Encode(r, test.subTypeHeader)
assert.NoError(t, err, "failed rlp.Encode for sub type Header")
oldEncodedData := r.Bytes()

r.Reset()

// new RLP encoding
err = test.header.EncodeRLP(r)
assert.NoError(t, err, "failed EncodeRLP of Header")
newEncodedData := r.Bytes()

assert.Equal(t, oldEncodedData, newEncodedData)
})
}
}
Loading

0 comments on commit 62dc9b2

Please sign in to comment.