From aa183c52be87f903f8694b6681ab774564958370 Mon Sep 17 00:00:00 2001
From: Arran Schlosberg <519948+ARR4N@users.noreply.github.com>
Date: Wed, 11 Dec 2024 10:20:58 +0000
Subject: [PATCH] refactor: generate internal `Header.encodeRLP()` for override
(#86)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Why this should be merged
This is a precursor to being able to override `types.Header` RLP
{en,de}coding. As there is already a `Header.EncodeRLP()` method we
either have to modify the generated code or rename the generated
method—this PR does the latter.
## How this works
The `rlpgen -internal_methods` flag changes the generated methods from
`EncodeRLP()` and `DecodeRLP()` to `encodeRLP()` and `decodeRLP()`,
respectively. A new CI job checks that generated code is up to date. We
can then implement our own `Header.EncodeRLP()` that either overrides or
falls back on the original.
It appears that `core/gen_genesis.go` was out of date but only because
of formatting.
## How this was tested
I deliberately excluded the change to `core/types/gen_header_rlp.go` to
confirm that the new workflow
[detects](https://github.com/ava-labs/libevm/actions/runs/12259667481/job/34202386378?pr=86#step:5:92)
the change and fails. The actual change can be inspected via the code
diff.
---
.github/workflows/go.yml | 17 +++++++++++
core/gen_genesis.go | 56 ++++++++++++++++++------------------
core/types/block.go | 2 +-
core/types/block.libevm.go | 29 +++++++++++++++++++
core/types/gen_header_rlp.go | 2 +-
rlp/rlpgen/gen.go | 9 ++++--
rlp/rlpgen/gen.libevm.go | 31 ++++++++++++++++++++
rlp/rlpgen/main.go | 5 ++++
8 files changed, 119 insertions(+), 32 deletions(-)
create mode 100644 core/types/block.libevm.go
create mode 100644 rlp/rlpgen/gen.libevm.go
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index ebdecbf3c70e..db48851ef50c 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -24,3 +24,20 @@ jobs:
- name: Run non-flaky tests concurrently
run: |
go test -short $(go list ./... | grep -Pv "${FLAKY_REGEX}");
+
+ go_generate:
+ env:
+ EXCLUDE_REGEX: 'ava-labs/libevm/(accounts/usbwallet/trezor)$'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: 1.21.4
+
+ - name: Run `go generate`
+ run: go list ./... | grep -Pv "${EXCLUDE_REGEX}" | xargs go generate;
+
+ - name: git diff
+ run: git diff --exit-code
diff --git a/core/gen_genesis.go b/core/gen_genesis.go
index 2e041d54bb35..9797f7914133 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -19,21 +19,21 @@ var _ = (*genesisSpecMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct {
- Config *params.ChainConfig `json:"config"`
- Nonce math.HexOrDecimal64 `json:"nonce"`
- Timestamp math.HexOrDecimal64 `json:"timestamp"`
- ExtraData hexutil.Bytes `json:"extraData"`
- GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
- Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
- Mixhash common.Hash `json:"mixHash"`
- Coinbase common.Address `json:"coinbase"`
+ Config *params.ChainConfig `json:"config"`
+ Nonce math.HexOrDecimal64 `json:"nonce"`
+ Timestamp math.HexOrDecimal64 `json:"timestamp"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
+ Mixhash common.Hash `json:"mixHash"`
+ Coinbase common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
- Number math.HexOrDecimal64 `json:"number"`
- GasUsed math.HexOrDecimal64 `json:"gasUsed"`
- ParentHash common.Hash `json:"parentHash"`
- BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
- ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
- BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
+ Number math.HexOrDecimal64 `json:"number"`
+ GasUsed math.HexOrDecimal64 `json:"gasUsed"`
+ ParentHash common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
+ BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
}
var enc Genesis
enc.Config = g.Config
@@ -62,21 +62,21 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON.
func (g *Genesis) UnmarshalJSON(input []byte) error {
type Genesis struct {
- Config *params.ChainConfig `json:"config"`
- Nonce *math.HexOrDecimal64 `json:"nonce"`
- Timestamp *math.HexOrDecimal64 `json:"timestamp"`
- ExtraData *hexutil.Bytes `json:"extraData"`
- GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
- Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
- Mixhash *common.Hash `json:"mixHash"`
- Coinbase *common.Address `json:"coinbase"`
+ Config *params.ChainConfig `json:"config"`
+ Nonce *math.HexOrDecimal64 `json:"nonce"`
+ Timestamp *math.HexOrDecimal64 `json:"timestamp"`
+ ExtraData *hexutil.Bytes `json:"extraData"`
+ GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
+ Mixhash *common.Hash `json:"mixHash"`
+ Coinbase *common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
- Number *math.HexOrDecimal64 `json:"number"`
- GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
- ParentHash *common.Hash `json:"parentHash"`
- BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
- ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
- BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
+ Number *math.HexOrDecimal64 `json:"number"`
+ GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
+ ParentHash *common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
+ BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
}
var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil {
diff --git a/core/types/block.go b/core/types/block.go
index 5563d9e28395..6be163bd60c0 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -59,7 +59,7 @@ 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
+//go:generate go run ../../rlp/rlpgen -type Header -internal_methods -out gen_header_rlp.go
// Header represents a block header in the Ethereum blockchain.
type Header struct {
diff --git a/core/types/block.libevm.go b/core/types/block.libevm.go
new file mode 100644
index 000000000000..adc5107a1369
--- /dev/null
+++ b/core/types/block.libevm.go
@@ -0,0 +1,29 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The libevm additions are distributed in the hope that they will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see
+// .
+
+package types
+
+import (
+ "io"
+
+ "github.com/ava-labs/libevm/rlp"
+)
+
+func (h *Header) EncodeRLP(w io.Writer) error {
+ return h.encodeRLP(w)
+}
+
+var _ rlp.Encoder = (*Header)(nil)
diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go
index 848f91e7ce99..e713e3d7ebbd 100644
--- a/core/types/gen_header_rlp.go
+++ b/core/types/gen_header_rlp.go
@@ -5,7 +5,7 @@ package types
import "github.com/ava-labs/libevm/rlp"
import "io"
-func (obj *Header) EncodeRLP(_w io.Writer) error {
+func (obj *Header) encodeRLP(_w io.Writer) error {
w := rlp.NewEncoderBuffer(_w)
_tmp0 := w.List()
w.WriteBytes(obj.ParentHash[:])
diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go
index 150797c7aa57..e641a0564f7e 100644
--- a/rlp/rlpgen/gen.go
+++ b/rlp/rlpgen/gen.go
@@ -35,6 +35,8 @@ type buildContext struct {
rawValueType *types.Named
typeToStructCache map[types.Type]*rlpstruct.Type
+
+ internalMethods bool
}
func newBuildContext(packageRLP *types.Package) *buildContext {
@@ -98,6 +100,8 @@ type genContext struct {
inPackage *types.Package
imports map[string]struct{}
tempCounter int
+
+ internalMethods bool
}
func newGenContext(inPackage *types.Package) *genContext {
@@ -736,7 +740,7 @@ func generateDecoder(ctx *genContext, typ string, op op) []byte {
result, code := op.genDecode(ctx)
var b bytes.Buffer
- fmt.Fprintf(&b, "func (obj *%s) DecodeRLP(dec *rlp.Stream) error {\n", typ)
+ fmt.Fprintf(&b, "func (obj *%s) %s(dec *rlp.Stream) error {\n", typ, ctx.decoderMethod())
fmt.Fprint(&b, code)
fmt.Fprintf(&b, " *obj = %s\n", result)
fmt.Fprintf(&b, " return nil\n")
@@ -751,7 +755,7 @@ func generateEncoder(ctx *genContext, typ string, op op) []byte {
ctx.addImport(pathOfPackageRLP)
var b bytes.Buffer
- fmt.Fprintf(&b, "func (obj *%s) EncodeRLP(_w io.Writer) error {\n", typ)
+ fmt.Fprintf(&b, "func (obj *%s) %s(_w io.Writer) error {\n", typ, ctx.encoderMethod())
fmt.Fprintf(&b, " w := rlp.NewEncoderBuffer(_w)\n")
fmt.Fprint(&b, op.genWrite(ctx, "obj"))
fmt.Fprintf(&b, " return w.Flush()\n")
@@ -773,6 +777,7 @@ func (bctx *buildContext) generate(typ *types.Named, encoder, decoder bool) ([]b
encSource []byte
decSource []byte
)
+ ctx.internalMethods = bctx.internalMethods
if encoder {
encSource = generateEncoder(ctx, typ.Obj().Name(), op)
}
diff --git a/rlp/rlpgen/gen.libevm.go b/rlp/rlpgen/gen.libevm.go
new file mode 100644
index 000000000000..63b52c4e1af3
--- /dev/null
+++ b/rlp/rlpgen/gen.libevm.go
@@ -0,0 +1,31 @@
+// Copyright 2024 the libevm authors.
+//
+// The libevm additions to go-ethereum are free software: you can redistribute
+// them and/or modify them under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The libevm additions are distributed in the hope that they will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see
+// .
+
+package main
+
+func (ctx *genContext) encoderMethod() string {
+ if ctx.internalMethods {
+ return "encodeRLP"
+ }
+ return "EncodeRLP"
+}
+
+func (ctx *genContext) decoderMethod() string {
+ if ctx.internalMethods {
+ return "decodeRLP"
+ }
+ return "DecodeRLP"
+}
diff --git a/rlp/rlpgen/main.go b/rlp/rlpgen/main.go
index 3e9310458afe..e9ac5cd0e650 100644
--- a/rlp/rlpgen/main.go
+++ b/rlp/rlpgen/main.go
@@ -36,6 +36,7 @@ func main() {
genEncoder = flag.Bool("encoder", true, "generate EncodeRLP?")
genDecoder = flag.Bool("decoder", false, "generate DecodeRLP?")
typename = flag.String("type", "", "type to generate methods for")
+ internal = flag.Bool("internal_methods", false, "generate internal (lower-case) method names")
)
flag.Parse()
@@ -44,6 +45,7 @@ func main() {
Type: *typename,
GenerateEncoder: *genEncoder,
GenerateDecoder: *genDecoder,
+ InternalMethods: *internal,
}
code, err := cfg.process()
if err != nil {
@@ -67,6 +69,8 @@ type Config struct {
GenerateEncoder bool
GenerateDecoder bool
+
+ InternalMethods bool
}
// process generates the Go code.
@@ -101,6 +105,7 @@ func (cfg *Config) process() (code []byte, err error) {
}
}
bctx := newBuildContext(packageRLP)
+ bctx.internalMethods = cfg.InternalMethods
// Find the type and generate.
typ, err := lookupStructType(pkg.Scope(), cfg.Type)