diff --git a/scripts/lint_allowed_geth_imports.sh b/scripts/lint_allowed_geth_imports.sh index 6603c86da6..ae26085f15 100755 --- a/scripts/lint_allowed_geth_imports.sh +++ b/scripts/lint_allowed_geth_imports.sh @@ -8,7 +8,7 @@ set -o pipefail # 1. Recursively search through all go files for any lines that include a direct import from go-ethereum # 2. Sort the unique results # #. Print out the difference between the search results and the list of specified allowed package imports from geth. -extra_imports=$(grep -r --include='*.go' --exclude-dir='simulator' '"github.com/ethereum/go-ethereum/.*"' -o -h | sort -u | comm -23 - ./scripts/geth-allowed-packages.txt) +extra_imports=$(grep -Pr --include='*.go' --exclude-dir='simulator' '^\s+"github.com/ethereum/go-ethereum/.*"' -o -h | tr -d '[:blank:]' | sort -u | comm -23 - ./scripts/geth-allowed-packages.txt) if [ -n "${extra_imports}" ]; then echo "new go-ethereum imports should be added to ./scripts/geth-allowed-packages.txt to prevent accidental imports:" echo "${extra_imports}" diff --git a/x/README.md b/x/README.md new file mode 100644 index 0000000000..fdc9180621 --- /dev/null +++ b/x/README.md @@ -0,0 +1,3 @@ +# Experimental + +Code in this directory is experimental and MUST NOT be relied on to be stable. \ No newline at end of file diff --git a/x/gethclone/README.md b/x/gethclone/README.md new file mode 100644 index 0000000000..bdfcb0ece3 --- /dev/null +++ b/x/gethclone/README.md @@ -0,0 +1,5 @@ +# `gethclone` + +This is an experimental module for tracking upstream `go-ethereum` changes. +The approach of `git merge`ing the upstream branch into `subnet-evm` is brittle as it relies on purely syntactic patching. +`gethclone` is intended to follow a rebase-like pattern, applying a set of semantic patches (e.g. AST modification, [Uber's `gopatch`](https://pkg.go.dev/github.com/uber-go/gopatch), etc.) that (a) should be more robust to refactoring; and (b) act as explicit documentation of the diffs. \ No newline at end of file diff --git a/x/gethclone/astpatch/astpatch.go b/x/gethclone/astpatch/astpatch.go new file mode 100644 index 0000000000..3bc6fd4e3a --- /dev/null +++ b/x/gethclone/astpatch/astpatch.go @@ -0,0 +1,91 @@ +// Package astpatch provides functionality for traversing and modifying Go +// syntax trees. It extends the astutil package with reusable "patches". +package astpatch + +import ( + "go/ast" + "reflect" + + "golang.org/x/tools/go/ast/astutil" +) + +type ( + // A Patch (optionally) modifies an AST node; it is equivalent to an + // `astutil.ApplyFunc` except that it returns an error instead of a boolean. + // A non-nil error is equivalent to returning false and will also abort all + // further calls to other patches. + Patch func(*astutil.Cursor) error + // A PatchRegistry maps [Go package path] -> [ast.Node concrete types] -> + // [all `Patch` functions that must be applied to said node types in said + // package]. + // + // The special `pkgPath` value "*" will match all package paths. + PatchRegistry map[string]map[reflect.Type][]Patch +) + +// Add is a convenience wrapper for registering a new `Patch` in the registry. +// The `zeroNode` can be any type (including nil pointers) that implements +// `ast.Node`. +// +// The special `pkgPath` value "*" will match all package paths. While there is +// no specific requirement for `pkgPath` other than it matching the equivalent +// argument passed to `Apply()`, it is typically sourced from +// `golang.org/x/tools/go/packages.Package.PkgPath`. +func (r PatchRegistry) Add(pkgPath string, zeroNode ast.Node, fn Patch) { + pkg, ok := r[pkgPath] + if !ok { + pkg = make(map[reflect.Type][]Patch) + r[pkgPath] = pkg + } + + t := nodeType(zeroNode) + pkg[t] = append(pkg[t], fn) +} + +// Apply calls `astutil.Apply()` on `node`, calling the appropriate `Patch` +// functions as the syntax tree is traversed. Patches are applied as the `pre` +// argument to `astutil.Apply()`. +// +// Global `pkgPath` matches (i.e. to those registered with "*") will be applied +// before package-specific matches. +// +// If any `Patch` returns an error then no further patches will be called, and +// the error will be returned by `Apply()`. +func (r PatchRegistry) Apply(pkgPath string, node ast.Node) error { + var err error + astutil.Apply(node, func(c *astutil.Cursor) bool { + if err != nil { + return false + } + if err = r.applyToCursor("*", c); err != nil { + return false + } + err = r.applyToCursor(pkgPath, c) + return err == nil + }, nil) + return err +} + +func (r PatchRegistry) applyToCursor(pkgPath string, c *astutil.Cursor) error { + if c.Node() == nil { + return nil + } + + pkg, ok := r[pkgPath] + if !ok { + return nil + } + for _, fn := range pkg[nodeType(c.Node())] { + if err := fn(c); err != nil { + return err + } + } + return nil +} + +// nodeType returns the `reflect.Type` of the _concrete_ type implementing +// `ast.Node`. Simpy calling `reflect.TypeOf(n)` would be incorrect as it would +// reflect the interface (and not match any nodes). +func nodeType(n ast.Node) reflect.Type { + return reflect.ValueOf(n).Type() +} diff --git a/x/gethclone/astpatch/astpatch_test.go b/x/gethclone/astpatch/astpatch_test.go new file mode 100644 index 0000000000..22ad63af40 --- /dev/null +++ b/x/gethclone/astpatch/astpatch_test.go @@ -0,0 +1,121 @@ +package astpatch + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/tools/go/ast/astutil" +) + +type patchSpy struct { + gotFuncs, gotStructs []string +} + +const errorIfFuncName = "ErrorFuncName" + +var errFuncName = fmt.Errorf("encountered sentinel function %q", errorIfFuncName) + +func (s *patchSpy) funcRecorder(c *astutil.Cursor) error { + name := c.Node().(*ast.FuncDecl).Name.String() + if name == errorIfFuncName { + return errFuncName + } + s.gotFuncs = append(s.gotFuncs, name) + return nil +} + +func (s *patchSpy) structRecorder(c *astutil.Cursor) error { + switch p := c.Parent().(type) { + case *ast.TypeSpec: // it's a `type x struct` not, for example, a `map[T]struct{}` + s.gotStructs = append(s.gotStructs, p.Name.String()) + } + return nil +} + +func TestPatchRegistry(t *testing.T) { + tests := []struct { + name string + src string + wantErr error + wantFuncs, wantStructs []string + }{ + { + name: "happy path", + src: `package thepackage + +func FnA(){} + +func FnB(){} + +type StructA struct{} + +type StructB struct{} +`, + wantFuncs: []string{"FnA", "FnB"}, + wantStructs: []string{"StructA", "StructB"}, + }, + { + name: "error propagation", + src: `package thepackage + +func HappyFn() {} + +func ` + errorIfFuncName + `() {} +`, + wantErr: errFuncName, + wantFuncs: []string{"HappyFn"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var spy patchSpy + reg := make(PatchRegistry) + + reg.Add("*", &ast.FuncDecl{}, spy.funcRecorder) + const pkgPath = `github.com/the/repo/thepackage` + reg.Add(pkgPath, &ast.StructType{}, spy.structRecorder) + + reg.Add("unknown/package/path", &ast.FuncDecl{}, func(c *astutil.Cursor) error { + t.Errorf("unexpected call to %T with different package path", (Patch)(nil)) + return nil + }) + + file := parseGoFile(t, token.NewFileSet(), tt.src) + bestEffortLogAST(t, file) + + // None of the `require.Equal*()` variants provide a check for exact + // match (i.e. equivalent to ==) of the identical error being + // propagated. + if gotErr := reg.Apply(pkgPath, file); gotErr != tt.wantErr { + t.Fatalf("%T.Apply(...) got err %v; want %v", reg, gotErr, tt.wantErr) + } + assert.Empty(t, cmp.Diff(tt.wantFuncs, spy.gotFuncs), "encountered function declarations (-want +got)") + assert.Empty(t, cmp.Diff(tt.wantStructs, spy.gotStructs), "encountered struct-type declarations (-want +got)") + }) + } +} + +func parseGoFile(tb testing.TB, fset *token.FileSet, src string) *ast.File { + tb.Helper() + f, err := parser.ParseFile(fset, "", src, parser.SkipObjectResolution) + require.NoError(tb, err, "Parsing Go source as file: parser.ParseFile([see logged source])") + return f +} + +func bestEffortLogAST(tb testing.TB, x any) { + tb.Helper() + + var buf bytes.Buffer + if err := ast.Fprint(&buf, nil, x, nil); err != nil { + return + } + tb.Logf("AST of parsed source:\n\n%s", buf.String()) +} diff --git a/x/gethclone/copyright.go.txt b/x/gethclone/copyright.go.txt new file mode 100644 index 0000000000..1c8b7b7cf3 --- /dev/null +++ b/x/gethclone/copyright.go.txt @@ -0,0 +1,14 @@ +// (c) 2019-2024, Ava Labs, Inc. +// +// This file is a derived work, based on the go-ethereum library whose original +// notices appear below. +// +// It is distributed under a license compatible with the licensing terms of the +// original code from which it is derived. +// +// Much love to the original authors for their work. +// ********** +// +// Code generated by gethclone - DO NOT EDIT. +// +// ********** \ No newline at end of file diff --git a/x/gethclone/gethclone.go b/x/gethclone/gethclone.go new file mode 100644 index 0000000000..1828df9c61 --- /dev/null +++ b/x/gethclone/gethclone.go @@ -0,0 +1,176 @@ +package main + +import ( + "context" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "log" + "os" + "path" + "path/filepath" + "strings" + + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/subnet-evm/x/gethclone/astpatch" + "go.uber.org/multierr" + "golang.org/x/mod/modfile" + "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/go/packages" + + _ "embed" + + // TODO(arr4n): change to using a git sub-module + _ "github.com/ethereum/go-ethereum/common" +) + +type config struct { + // Externally configurable (e.g. flags) + packages []string + outputGoMod string + + // Internal + outputModule *modfile.Module + astPatches astpatch.PatchRegistry + + processed map[string]bool +} + +const geth = "github.com/ethereum/go-ethereum" + +func (c *config) run(ctx context.Context) error { + for i, p := range c.packages { + if !strings.HasPrefix(p, geth) { + c.packages[i] = path.Join(geth, p) + } + } + + mod, err := parseGoMod(c.outputGoMod) + fmt.Println(err) + if err != nil { + return nil + } + c.outputModule = mod.Module + + c.processed = make(map[string]bool) + return c.loadAndParse(ctx, token.NewFileSet(), c.packages...) +} + +func parseGoMod(filePath string) (*modfile.File, error) { + buf, err := os.ReadFile(filePath) + if err != nil { + return nil, fmt.Errorf("parsing output go.mod: os.ReadFile(%q): %v", filePath, err) + } + return modfile.ParseLax(filePath, buf, nil) +} + +// loadAndParse loads all packages that match the `patterns` and individually +// passes them to `c.parse()`. +func (c *config) loadAndParse(ctx context.Context, fset *token.FileSet, patterns ...string) error { + if len(patterns) == 0 { + return nil + } + + pkgConfig := &packages.Config{ + Context: ctx, + Mode: packages.NeedName | packages.NeedCompiledGoFiles, + } + pkgs, err := packages.Load(pkgConfig, patterns...) + if err != nil { + return fmt.Errorf("packages.Load(..., %q): %v", c.packages, err) + } + + for _, pkg := range pkgs { + if err := c.parse(ctx, pkg, fset); err != nil { + return err + } + } + return nil +} + +//go:embed copyright.go.txt +var copyrightHeader string + +// parse parses all `pkg.Files` into `fset`, transforms each according to +// semantic patches, and passes all geth imports back to `c.loadAndParse()` for +// recursive handling. +func (c *config) parse(ctx context.Context, pkg *packages.Package, fset *token.FileSet) error { + if len(pkg.Errors) != 0 { + var err error + for _, e := range pkg.Errors { + multierr.AppendInto(&err, e) + } + return err + } + + if c.processed[pkg.PkgPath] { + return nil + } + c.processed[pkg.PkgPath] = true + + outDir := filepath.Join( + filepath.Dir(c.outputGoMod), + strings.TrimPrefix(pkg.PkgPath, geth), + ) + log.Printf("Cloning %q into %q", pkg.PkgPath, outDir) + + allGethImports := set.NewSet[string](0) + for _, fName := range pkg.CompiledGoFiles { + file, err := parser.ParseFile(fset, fName, nil, parser.ParseComments|parser.SkipObjectResolution) + if err != nil { + return fmt.Errorf("parser.ParseFile(... %q ...): %v", fName, err) + } + + gethImports, err := c.transformGethImports(fset, file) + if err != nil { + return nil + } + allGethImports.Union(gethImports) + + file.Comments = append([]*ast.CommentGroup{{ + List: []*ast.Comment{{Text: copyrightHeader}}, + }}, file.Comments...) + + if err := c.astPatches.Apply(pkg.PkgPath, file); err != nil { + return fmt.Errorf("apply AST patches to %q: %v", pkg.PkgPath, err) + } + + if false { + // TODO(arr4n): enable writing when a suitable strategy exists; in + // the meantime, this code is demonstrative of intent. Do we want to + // simply overwrite and then check `git diff`? Do we want to check + // the diffs here? + outFile := filepath.Join(outDir, filepath.Base(fName)) + f, err := os.Create(outFile) + if err != nil { + return err + } + if err := format.Node(f, fset, file); err != nil { + return err + } + } + } + + return c.loadAndParse(ctx, fset, allGethImports.List()...) +} + +// transformGethImports finds all `ethereum/go-ethereum` imports in the file, +// converts their path to `c.outputModule`, and returns the set of transformed +// import paths. +func (c *config) transformGethImports(fset *token.FileSet, file *ast.File) (set.Set[string], error) { + imports := set.NewSet[string](len(file.Imports)) + for _, im := range file.Imports { + p := strings.Trim(im.Path.Value, `"`) + if !strings.HasPrefix(p, geth) { + continue + } + + imports.Add(p) + if !astutil.RewriteImport(fset, file, p, strings.Replace(p, geth, c.outputModule.Mod.String(), 1)) { + return nil, fmt.Errorf("failed to rewrite import %q", p) + } + } + return imports, nil +} diff --git a/x/gethclone/go.mod b/x/gethclone/go.mod new file mode 100644 index 0000000000..0c99cc4c69 --- /dev/null +++ b/x/gethclone/go.mod @@ -0,0 +1,31 @@ +module github.com/ava-labs/subnet-evm/x/gethclone + +go 1.22.4 + +require ( + github.com/ava-labs/avalanchego v1.11.8 + github.com/ethereum/go-ethereum v1.13.8 + github.com/google/go-cmp v0.6.0 + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.8.4 + go.uber.org/multierr v1.10.0 + golang.org/x/mod v0.18.0 + golang.org/x/tools v0.22.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/renameio/v2 v2.0.0 // indirect + github.com/gorilla/rpc v1.2.0 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.uber.org/mock v0.4.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + gonum.org/v1/gonum v0.11.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/x/gethclone/go.sum b/x/gethclone/go.sum new file mode 100644 index 0000000000..48b7852757 --- /dev/null +++ b/x/gethclone/go.sum @@ -0,0 +1,58 @@ +github.com/ava-labs/avalanchego v1.11.8 h1:Q/der5bC/q3BQbIqxT7nNC0F30c+6X1G/eQzzMQ2CLk= +github.com/ava-labs/avalanchego v1.11.8/go.mod h1:aPYTETkM0KjtC7vFwPO6S8z2L0QTKaXjVUi98pTdNO4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= +github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= +github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= +github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU= +github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/thepudds/fzgen v0.4.2 h1:HlEHl5hk2/cqEomf2uK5SA/FeJc12s/vIHmOG+FbACw= +github.com/thepudds/fzgen v0.4.2/go.mod h1:kHCWdsv5tdnt32NIHYDdgq083m6bMtaY0M+ipiO9xWE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/x/gethclone/main.go b/x/gethclone/main.go new file mode 100644 index 0000000000..c24516f18f --- /dev/null +++ b/x/gethclone/main.go @@ -0,0 +1,29 @@ +// The gethclone binary clones ethereum/go-ethereum Go packages, applying +// semantic patches. +package main + +import ( + "context" + "log" + "os" + + "github.com/ava-labs/subnet-evm/x/gethclone/astpatch" + "github.com/spf13/pflag" +) + +func main() { + c := config{ + astPatches: make(astpatch.PatchRegistry), + } + + pflag.StringSliceVar(&c.packages, "packages", []string{"core/vm"}, `Geth packages to clone, with or without "github.com/ethereum/go-ethereum" prefix.`) + pflag.StringVar(&c.outputGoMod, "output_go_mod", "", "go.mod file of the destination to which geth will be cloned.") + pflag.Parse() + + log.SetOutput(os.Stderr) + log.Print("START") + if err := c.run(context.Background()); err != nil { + log.Fatal(err) + } + log.Print("DONE") +}