Skip to content

Commit ea7071e

Browse files
author
Darioush Jalali
committed
rlp "custom handling via registration" idea
1 parent 594abd9 commit ea7071e

File tree

6 files changed

+350
-3
lines changed

6 files changed

+350
-3
lines changed

core/types/block.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,37 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
5858
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
5959
}
6060

61+
type HeaderSerializer interface {
62+
EncodeRLP(h *Header, w io.Writer) error
63+
DecodeRLP(h *Header, s *rlp.Stream) error
64+
}
65+
66+
var RegisteredHeaderSerializer HeaderSerializer
67+
68+
// XXX: JSON marshalling should be handled as well.
6169
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
62-
//go:generate go run ../../rlp/rlpgen -type Header -out gen_header_rlp.go
70+
//go:generate go run ../../rlp/rlpgen -type Header_ -out gen_header_rlp.go
6371

6472
// Header represents a block header in the Ethereum blockchain.
65-
type Header struct {
73+
type Header HeaderWithExtraPayload
74+
75+
func (obj *Header) EncodeRLP(w io.Writer) error {
76+
if RegisteredHeaderSerializer != nil {
77+
return RegisteredHeaderSerializer.EncodeRLP(obj, w)
78+
}
79+
return rlp.Encode(w, (*HeaderWithExtraPayload)(obj))
80+
}
81+
82+
func (obj *Header) DecodeRLP(s *rlp.Stream) error {
83+
if RegisteredHeaderSerializer != nil {
84+
return RegisteredHeaderSerializer.DecodeRLP(obj, s)
85+
}
86+
return s.Decode((*HeaderWithExtraPayload)(obj))
87+
}
88+
89+
type HeaderWithExtraPayload struct { // Note this name must be exported if we want to use rlpgen
90+
ExtraPayload interface{} `json:"-" rlp:"-"`
91+
6692
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
6793
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
6894
Coinbase common.Address `json:"miner"`

core/types/gen_header_json.go

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

core/types/gen_header_rlp.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/types_ext/block.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2014 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// Package types contains data types related to Ethereum consensus.
18+
package types_ext
19+
20+
import (
21+
"io"
22+
"math/big"
23+
24+
"github.com/ava-labs/libevm/common"
25+
"github.com/ava-labs/libevm/core/types"
26+
"github.com/ava-labs/libevm/rlp"
27+
)
28+
29+
type (
30+
BlockNonce = types.BlockNonce
31+
Bloom = types.Bloom
32+
)
33+
34+
func init() {
35+
// Register the header type for RLP serialization
36+
types.RegisteredHeaderSerializer = headerSerializable{}
37+
}
38+
39+
//go:generate go run ../../rlp/rlpgen -type headerSerializable -out gen_header_rlp.go
40+
type Header = types.Header
41+
42+
func (headerSerializable) EncodeRLP(h *types.Header, w io.Writer) error {
43+
var hs headerSerializable
44+
45+
// Set the shared fields with upstream
46+
hs.ParentHash = h.ParentHash
47+
hs.UncleHash = h.UncleHash
48+
hs.Coinbase = h.Coinbase
49+
hs.Root = h.Root
50+
hs.TxHash = h.TxHash
51+
hs.ReceiptHash = h.ReceiptHash
52+
hs.Bloom = h.Bloom
53+
hs.Difficulty = h.Difficulty
54+
hs.Number = h.Number
55+
hs.GasLimit = h.GasLimit
56+
hs.GasUsed = h.GasUsed
57+
hs.Time = h.Time
58+
hs.Extra = h.Extra
59+
hs.MixDigest = h.MixDigest
60+
61+
// Set the extra payload
62+
if h.ExtraPayload != nil {
63+
hs.AddedStuff = h.ExtraPayload.(*headerExtra).AddedStuff
64+
}
65+
66+
return rlp.Encode(w, &hs)
67+
}
68+
69+
func (headerSerializable) DecodeRLP(h *types.Header, s *rlp.Stream) error {
70+
var hs headerSerializable
71+
if err := s.Decode(&hs); err != nil {
72+
return err
73+
}
74+
75+
// Set the shared fields with upstream
76+
h.ParentHash = hs.ParentHash
77+
h.UncleHash = hs.UncleHash
78+
h.Coinbase = hs.Coinbase
79+
h.Root = hs.Root
80+
h.TxHash = hs.TxHash
81+
h.ReceiptHash = hs.ReceiptHash
82+
h.Bloom = hs.Bloom
83+
h.Difficulty = hs.Difficulty
84+
h.Number = hs.Number
85+
h.GasLimit = hs.GasLimit
86+
h.GasUsed = hs.GasUsed
87+
h.Time = hs.Time
88+
h.Extra = hs.Extra
89+
h.MixDigest = hs.MixDigest
90+
h.Nonce = hs.Nonce
91+
h.BaseFee = hs.BaseFee
92+
h.BlobGasUsed = hs.BlobGasUsed
93+
h.ExcessBlobGas = hs.ExcessBlobGas
94+
h.ParentBeaconRoot = hs.ParentBeaconRoot
95+
96+
// Set the extra payload
97+
h.ExtraPayload = &headerExtra{
98+
AddedStuff: hs.AddedStuff,
99+
}
100+
return nil
101+
}
102+
103+
// Separate the extra payload from the header, so there is no duplicate of
104+
// shared fields.
105+
type headerExtra struct {
106+
AddedStuff *common.Hash
107+
}
108+
109+
// The purpose of this struct is to specify the exact RLP serialization of the
110+
// header, including shared fields and fields related to the extra payload.
111+
type headerSerializable struct {
112+
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
113+
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
114+
Coinbase common.Address `json:"miner"`
115+
Root common.Hash `json:"stateRoot" gencodec:"required"`
116+
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
117+
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
118+
Bloom Bloom `json:"logsBloom" gencodec:"required"`
119+
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
120+
Number *big.Int `json:"number" gencodec:"required"`
121+
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
122+
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
123+
Time uint64 `json:"timestamp" gencodec:"required"`
124+
Extra []byte `json:"extraData" gencodec:"required"`
125+
MixDigest common.Hash `json:"mixHash"`
126+
Nonce BlockNonce `json:"nonce"`
127+
128+
// Added stuff in the middle of the header
129+
AddedStuff *common.Hash `json:"addedStuff" rlp:"optional"`
130+
131+
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
132+
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
133+
134+
// BlobGasUsed was added by EIP-4844 and is ignored in legacy headers.
135+
BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"`
136+
137+
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
138+
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
139+
140+
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
141+
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
142+
}
143+
144+
// XXX: Need to support size too in upstream code
145+
// var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())

core/types_ext/block_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package types_ext
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/ava-labs/libevm/common"
8+
"github.com/ava-labs/libevm/rlp"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestRLP(t *testing.T) {
13+
// expected (eg, from network or disk)
14+
added := common.Hash{111}
15+
asIs := headerSerializable{
16+
Number: big.NewInt(10), // Shared field
17+
AddedStuff: &added, // Added field
18+
}
19+
20+
rlpAsIs, err := rlp.EncodeToBytes(&asIs)
21+
require.NoError(t, err)
22+
23+
// now parse it via upstream type
24+
var upstream Header
25+
err = rlp.DecodeBytes(rlpAsIs, &upstream)
26+
require.NoError(t, err)
27+
28+
// check that the fields are the same
29+
require.Equal(t, asIs.Number, upstream.Number)
30+
require.Equal(t, asIs.AddedStuff, upstream.ExtraPayload.(*headerExtra).AddedStuff)
31+
32+
// now encode the upstream type
33+
rlpUpstream, err := rlp.EncodeToBytes(&upstream)
34+
require.NoError(t, err)
35+
require.Equal(t, rlpAsIs, rlpUpstream)
36+
}

0 commit comments

Comments
 (0)