diff --git a/core/types/block.go b/core/types/block.go index 4857cd6e50..85fe676cf8 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -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 { diff --git a/core/types/celo_block.go b/core/types/celo_block.go index 075c856087..c6985674c7 100644 --- a/core/types/celo_block.go +++ b/core/types/celo_block.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "io" "math/big" @@ -8,9 +9,12 @@ import ( "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"` @@ -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 @@ -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 @@ -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 @@ -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, @@ -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, @@ -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 diff --git a/core/types/celo_block_test.go b/core/types/celo_block_test.go new file mode 100644 index 0000000000..233b844dd4 --- /dev/null +++ b/core/types/celo_block_test.go @@ -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) + }) + } +} diff --git a/core/types/gen_after_gingerbread_header_rlp.go b/core/types/gen_after_gingerbread_header_rlp.go new file mode 100644 index 0000000000..e0215a4ddb --- /dev/null +++ b/core/types/gen_after_gingerbread_header_rlp.go @@ -0,0 +1,230 @@ +// Code generated by rlpgen. DO NOT EDIT. + +package types + +import "github.com/ethereum/go-ethereum/common" +import "github.com/ethereum/go-ethereum/rlp" +import "io" + +func (obj *AfterGingerbreadHeader) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.WriteBytes(obj.ParentHash[:]) + w.WriteBytes(obj.UncleHash[:]) + w.WriteBytes(obj.Coinbase[:]) + w.WriteBytes(obj.Root[:]) + w.WriteBytes(obj.TxHash[:]) + w.WriteBytes(obj.ReceiptHash[:]) + w.WriteBytes(obj.Bloom[:]) + if obj.Difficulty == nil { + w.Write(rlp.EmptyString) + } else { + if obj.Difficulty.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.Difficulty) + } + if obj.Number == nil { + w.Write(rlp.EmptyString) + } else { + if obj.Number.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.Number) + } + w.WriteUint64(obj.GasLimit) + w.WriteUint64(obj.GasUsed) + w.WriteUint64(obj.Time) + w.WriteBytes(obj.Extra) + w.WriteBytes(obj.MixDigest[:]) + w.WriteBytes(obj.Nonce[:]) + _tmp1 := obj.BaseFee != nil + _tmp2 := obj.WithdrawalsHash != nil + _tmp3 := obj.BlobGasUsed != nil + _tmp4 := obj.ExcessBlobGas != nil + _tmp5 := obj.ParentBeaconRoot != nil + if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 { + if obj.BaseFee == nil { + w.Write(rlp.EmptyString) + } else { + if obj.BaseFee.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.BaseFee) + } + } + if _tmp2 || _tmp3 || _tmp4 || _tmp5 { + if obj.WithdrawalsHash == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.WithdrawalsHash[:]) + } + } + if _tmp3 || _tmp4 || _tmp5 { + if obj.BlobGasUsed == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.BlobGasUsed)) + } + } + if _tmp4 || _tmp5 { + if obj.ExcessBlobGas == nil { + w.Write([]byte{0x80}) + } else { + w.WriteUint64((*obj.ExcessBlobGas)) + } + } + if _tmp5 { + if obj.ParentBeaconRoot == nil { + w.Write([]byte{0x80}) + } else { + w.WriteBytes(obj.ParentBeaconRoot[:]) + } + } + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *AfterGingerbreadHeader) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 AfterGingerbreadHeader + { + if _, err := dec.List(); err != nil { + return err + } + // ParentHash: + var _tmp1 common.Hash + if err := dec.ReadBytes(_tmp1[:]); err != nil { + return err + } + _tmp0.ParentHash = _tmp1 + // UncleHash: + var _tmp2 common.Hash + if err := dec.ReadBytes(_tmp2[:]); err != nil { + return err + } + _tmp0.UncleHash = _tmp2 + // Coinbase: + var _tmp3 common.Address + if err := dec.ReadBytes(_tmp3[:]); err != nil { + return err + } + _tmp0.Coinbase = _tmp3 + // Root: + var _tmp4 common.Hash + if err := dec.ReadBytes(_tmp4[:]); err != nil { + return err + } + _tmp0.Root = _tmp4 + // TxHash: + var _tmp5 common.Hash + if err := dec.ReadBytes(_tmp5[:]); err != nil { + return err + } + _tmp0.TxHash = _tmp5 + // ReceiptHash: + var _tmp6 common.Hash + if err := dec.ReadBytes(_tmp6[:]); err != nil { + return err + } + _tmp0.ReceiptHash = _tmp6 + // Bloom: + var _tmp7 Bloom + if err := dec.ReadBytes(_tmp7[:]); err != nil { + return err + } + _tmp0.Bloom = _tmp7 + // Difficulty: + _tmp8, err := dec.BigInt() + if err != nil { + return err + } + _tmp0.Difficulty = _tmp8 + // Number: + _tmp9, err := dec.BigInt() + if err != nil { + return err + } + _tmp0.Number = _tmp9 + // GasLimit: + _tmp10, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.GasLimit = _tmp10 + // GasUsed: + _tmp11, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.GasUsed = _tmp11 + // Time: + _tmp12, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.Time = _tmp12 + // Extra: + _tmp13, err := dec.Bytes() + if err != nil { + return err + } + _tmp0.Extra = _tmp13 + // MixDigest: + var _tmp14 common.Hash + if err := dec.ReadBytes(_tmp14[:]); err != nil { + return err + } + _tmp0.MixDigest = _tmp14 + // Nonce: + var _tmp15 BlockNonce + if err := dec.ReadBytes(_tmp15[:]); err != nil { + return err + } + _tmp0.Nonce = _tmp15 + // BaseFee: + if dec.MoreDataInList() { + _tmp16, err := dec.BigInt() + if err != nil { + return err + } + _tmp0.BaseFee = _tmp16 + // WithdrawalsHash: + if dec.MoreDataInList() { + var _tmp17 common.Hash + if err := dec.ReadBytes(_tmp17[:]); err != nil { + return err + } + _tmp0.WithdrawalsHash = &_tmp17 + // BlobGasUsed: + if dec.MoreDataInList() { + _tmp18, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.BlobGasUsed = &_tmp18 + // ExcessBlobGas: + if dec.MoreDataInList() { + _tmp19, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.ExcessBlobGas = &_tmp19 + // ParentBeaconRoot: + if dec.MoreDataInList() { + var _tmp20 common.Hash + if err := dec.ReadBytes(_tmp20[:]); err != nil { + return err + } + _tmp0.ParentBeaconRoot = &_tmp20 + } + } + } + } + } + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +} diff --git a/core/types/gen_before_gingerbread_header_rlp.go b/core/types/gen_before_gingerbread_header_rlp.go new file mode 100644 index 0000000000..2a0014b48a --- /dev/null +++ b/core/types/gen_before_gingerbread_header_rlp.go @@ -0,0 +1,105 @@ +// Code generated by rlpgen. DO NOT EDIT. + +package types + +import "github.com/ethereum/go-ethereum/common" +import "github.com/ethereum/go-ethereum/rlp" +import "io" + +func (obj *BeforeGingerbreadHeader) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.WriteBytes(obj.ParentHash[:]) + w.WriteBytes(obj.Coinbase[:]) + w.WriteBytes(obj.Root[:]) + w.WriteBytes(obj.TxHash[:]) + w.WriteBytes(obj.ReceiptHash[:]) + w.WriteBytes(obj.Bloom[:]) + if obj.Number == nil { + w.Write(rlp.EmptyString) + } else { + if obj.Number.Sign() == -1 { + return rlp.ErrNegativeBigInt + } + w.WriteBigInt(obj.Number) + } + w.WriteUint64(obj.GasUsed) + w.WriteUint64(obj.Time) + w.WriteBytes(obj.Extra) + w.ListEnd(_tmp0) + return w.Flush() +} + +func (obj *BeforeGingerbreadHeader) DecodeRLP(dec *rlp.Stream) error { + var _tmp0 BeforeGingerbreadHeader + { + if _, err := dec.List(); err != nil { + return err + } + // ParentHash: + var _tmp1 common.Hash + if err := dec.ReadBytes(_tmp1[:]); err != nil { + return err + } + _tmp0.ParentHash = _tmp1 + // Coinbase: + var _tmp2 common.Address + if err := dec.ReadBytes(_tmp2[:]); err != nil { + return err + } + _tmp0.Coinbase = _tmp2 + // Root: + var _tmp3 common.Hash + if err := dec.ReadBytes(_tmp3[:]); err != nil { + return err + } + _tmp0.Root = _tmp3 + // TxHash: + var _tmp4 common.Hash + if err := dec.ReadBytes(_tmp4[:]); err != nil { + return err + } + _tmp0.TxHash = _tmp4 + // ReceiptHash: + var _tmp5 common.Hash + if err := dec.ReadBytes(_tmp5[:]); err != nil { + return err + } + _tmp0.ReceiptHash = _tmp5 + // Bloom: + var _tmp6 Bloom + if err := dec.ReadBytes(_tmp6[:]); err != nil { + return err + } + _tmp0.Bloom = _tmp6 + // Number: + _tmp7, err := dec.BigInt() + if err != nil { + return err + } + _tmp0.Number = _tmp7 + // GasUsed: + _tmp8, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.GasUsed = _tmp8 + // Time: + _tmp9, err := dec.Uint64() + if err != nil { + return err + } + _tmp0.Time = _tmp9 + // Extra: + _tmp10, err := dec.Bytes() + if err != nil { + return err + } + _tmp0.Extra = _tmp10 + if err := dec.ListEnd(); err != nil { + return err + } + } + *obj = _tmp0 + return nil +}