Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: align accounts/abi/bind with coreth+upstream #1427

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 21 additions & 69 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,14 @@ import (
"github.com/ethereum/go-ethereum/log"
)

// BindHook is a callback function that can be used to customize the binding.
type BindHook func(lang Lang, pkg string, types []string, contracts map[string]*TmplContract, structs map[string]*TmplStruct) (data interface{}, templateSource string, err error)

// Lang is a target programming language selector to generate bindings for.
type Lang int

const (
LangGo Lang = iota
)

func IsKeyWord(arg string) bool {
func isKeyWord(arg string) bool {
switch arg {
case "break":
case "case":
Expand Down Expand Up @@ -101,10 +98,10 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string, bindHook BindHook) (string, error) {
var (
// contracts is the map of each individual contract requested binding
contracts = make(map[string]*TmplContract)
contracts = make(map[string]*tmplContract)

// structs is the map of all redeclared structs shared by passed contracts.
structs = make(map[string]*TmplStruct)
structs = make(map[string]*tmplStruct)

// isLib is the map used to flag each encountered library as such
isLib = make(map[string]struct{})
Expand All @@ -125,11 +122,11 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s

// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
var (
calls = make(map[string]*TmplMethod)
transacts = make(map[string]*TmplMethod)
calls = make(map[string]*tmplMethod)
transacts = make(map[string]*tmplMethod)
events = make(map[string]*tmplEvent)
fallback *TmplMethod
receive *TmplMethod
fallback *tmplMethod
receive *tmplMethod

// identifiers are used to detect duplicated identifiers of functions
// and events. For all calls, transacts and events, abigen will generate
Expand Down Expand Up @@ -172,7 +169,7 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
if input.Name == "" || IsKeyWord(input.Name) {
if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
}
if hasStruct(input.Type) {
Expand All @@ -191,9 +188,9 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s
}
// Append the methods to the call or transact lists
if original.IsConstant() {
calls[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
} else {
transacts[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
}
}
for _, original := range evmABI.Events {
Expand Down Expand Up @@ -224,7 +221,7 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
if input.Name == "" || IsKeyWord(input.Name) {
if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
}
// Event is a bit special, we need to define event struct in binding,
Expand All @@ -245,12 +242,12 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s
}
// Add two special fallback functions if they exist
if evmABI.HasFallback() {
fallback = &TmplMethod{Original: evmABI.Fallback}
fallback = &tmplMethod{Original: evmABI.Fallback}
}
if evmABI.HasReceive() {
receive = &TmplMethod{Original: evmABI.Receive}
receive = &tmplMethod{Original: evmABI.Receive}
}
contracts[types[i]] = &TmplContract{
contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
Expand Down Expand Up @@ -341,14 +338,10 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s

// bindType is a set of type binders that convert Solidity types to some supported
// programming language types.
var bindType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindTypeGo,
}

var bindTypeNew = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{
LangGo: bindTypeNewGo,
}

// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
func bindBasicTypeGo(kind abi.Type) string {
switch kind.T {
Expand All @@ -373,43 +366,10 @@ func bindBasicTypeGo(kind abi.Type) string {
}
}

// bindTypeNewGo converts new types to Go ones.
func bindTypeNewGo(kind abi.Type, structs map[string]*TmplStruct) string {
switch kind.T {
case abi.TupleTy:
return structs[kind.TupleRawName+kind.String()].Name + "{}"
case abi.ArrayTy:
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) + "{}"
case abi.SliceTy:
return "nil"
case abi.AddressTy:
return "common.Address{}"
case abi.IntTy, abi.UintTy:
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
switch parts[2] {
case "8", "16", "32", "64":
return "0"
}
return "new(big.Int)"
case abi.FixedBytesTy:
return fmt.Sprintf("[%d]byte", kind.Size) + "{}"
case abi.BytesTy:
return "[]byte{}"
case abi.FunctionTy:
return "[24]byte{}"
case abi.BoolTy:
return "false"
case abi.StringTy:
return `""`
default:
return "nil"
}
}

// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. BigDecimal).
func bindTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {
func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
return structs[kind.TupleRawName+kind.String()].Name
Expand All @@ -424,13 +384,13 @@ func bindTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {

// bindTopicType is a set of type binders that convert Solidity types to some
// supported programming language topic types.
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindTopicTypeGo,
}

// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
// functionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {
func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
bound := bindTypeGo(kind, structs)

// todo(rjl493456442) according solidity documentation, indexed event
Expand All @@ -447,14 +407,14 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {

// bindStructType is a set of type binders that convert Solidity tuple types to some supported
// programming language struct definition.
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindStructTypeGo,
}

// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
// in the given map.
// Notably, this function will resolve and record nested struct recursively.
func bindStructTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {
func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
// We compose a raw struct name and a canonical parameter expression
Expand Down Expand Up @@ -483,7 +443,7 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*TmplStruct) string {
}
name = capitalise(name)

structs[id] = &TmplStruct{
structs[id] = &tmplStruct{
Name: name,
Fields: fields,
}
Expand Down Expand Up @@ -568,11 +528,3 @@ func hasStruct(t abi.Type) bool {
return false
}
}

func mkList(args ...interface{}) []interface{} {
return args
}

func add(a, b int) int {
return a + b
}
70 changes: 70 additions & 0 deletions accounts/abi/bind/bind_extra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// (c) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package bind

import (
"fmt"
"regexp"

"github.com/ava-labs/subnet-evm/accounts/abi"
)

type (
// These types are exported for use in bind/precompilebind
TmplContract = tmplContract
TmplMethod = tmplMethod
TmplStruct = tmplStruct
)

// BindHook is a callback function that can be used to customize the binding.
type BindHook func(lang Lang, pkg string, types []string, contracts map[string]*tmplContract, structs map[string]*tmplStruct) (data any, templateSource string, err error)

func IsKeyWord(arg string) bool {
return isKeyWord(arg)
}

var bindTypeNew = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
LangGo: bindTypeNewGo,
}

// bindTypeNewGo converts new types to Go ones.
func bindTypeNewGo(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T {
case abi.TupleTy:
return structs[kind.TupleRawName+kind.String()].Name + "{}"
case abi.ArrayTy:
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) + "{}"
case abi.SliceTy:
return "nil"
case abi.AddressTy:
return "common.Address{}"
case abi.IntTy, abi.UintTy:
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
switch parts[2] {
case "8", "16", "32", "64":
return "0"
}
return "new(big.Int)"
case abi.FixedBytesTy:
return fmt.Sprintf("[%d]byte", kind.Size) + "{}"
case abi.BytesTy:
return "[]byte{}"
case abi.FunctionTy:
return "[24]byte{}"
case abi.BoolTy:
return "false"
case abi.StringTy:
return `""`
default:
return "nil"
}
}

func mkList(args ...any) []any {
return args
}

func add(a, b int) int {
return a + b
}
24 changes: 12 additions & 12 deletions accounts/abi/bind/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,30 @@ import "github.com/ava-labs/subnet-evm/accounts/abi"
// tmplData is the data structure required to fill the binding template.
type tmplData struct {
Package string // Name of the package to place the generated file in
Contracts map[string]*TmplContract // List of contracts to generate into this file
Contracts map[string]*tmplContract // List of contracts to generate into this file
Libraries map[string]string // Map the bytecode's link pattern to the library name
Structs map[string]*TmplStruct // Contract struct type definitions
Structs map[string]*tmplStruct // Contract struct type definitions
}

// TmplContract contains the data needed to generate an individual contract binding.
type TmplContract struct {
// tmplContract contains the data needed to generate an individual contract binding.
type tmplContract struct {
Type string // Type name of the main contract binding
InputABI string // JSON ABI used as the input to generate the binding from
InputBin string // Optional EVM bytecode used to generate deploy code from
FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*TmplMethod // Contract calls that only read state data
Transacts map[string]*TmplMethod // Contract calls that write state data
Fallback *TmplMethod // Additional special fallback function
Receive *TmplMethod // Additional special receive function
Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data
Fallback *tmplMethod // Additional special fallback function
Receive *tmplMethod // Additional special receive function
Events map[string]*tmplEvent // Contract events accessors
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
Library bool // Indicator whether the contract is a library
}

// TmplMethod is a wrapper around an abi.Method that contains a few preprocessed
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
// and cached data fields.
type TmplMethod struct {
type tmplMethod struct {
Original abi.Method // Original method as parsed by the abi package
Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
Structured bool // Whether the returns should be accumulated into a struct
Expand All @@ -75,9 +75,9 @@ type tmplField struct {
SolKind abi.Type // Raw abi type information
}

// TmplStruct is a wrapper around an abi.tuple and contains an auto-generated
// tmplStruct is a wrapper around an abi.tuple and contains an auto-generated
// struct name.
type TmplStruct struct {
type tmplStruct struct {
Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
Fields []*tmplField // Struct fields definition depends on the binding language.
}
Expand Down
1 change: 0 additions & 1 deletion cmd/abigen/namefilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the removal? The geth files seem to have no copyright at all, so I'm not sure why we would modify this 🤔

Copy link
Collaborator Author

@darioush darioush Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to match coreth.

package main

import (
Expand Down
1 change: 0 additions & 1 deletion cmd/abigen/namefilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// original code from which it is derived.
//
// Much love to the original authors for their work.
// **********
qdm12 marked this conversation as resolved.
Show resolved Hide resolved
package main

import (
Expand Down
2 changes: 1 addition & 1 deletion triedb/pathdb/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode
return root, ctx.nodes, triestate.New(ctx.accountOrigin, ctx.storageOrigin, nil)
}

// lastRoot returns the latest root hash, or empty if nothing is cached.
// lastHash returns the latest root hash, or empty if nothing is cached.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a typo in geth, I propose we revert this, however, if you want, I can be the typo comment man and accumulate these in a branch on the libevm repository, that we can PR to geth when it gets large enough?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coreth already contains this typo fix, so I suppose why not then. Ideally we should revert the fix in coreth but that seems like a lot of trouble for just this.

Anyway, I opened ava-labs/libevm#108 feel free to commit to it when you find a geth code typo.

Copy link
Collaborator Author

@darioush darioush Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, here I'm just trying to reduce the difference in our repos.
We should try to take the geth changes as part of geth updates and fix them in upstream PRs.

func (t *tester) lastHash() common.Hash {
if len(t.roots) == 0 {
return common.Hash{}
Expand Down
Loading