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

add precompile bind tests #788

Closed
wants to merge 6 commits into from
Closed
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
44 changes: 41 additions & 3 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const (
LangGo Lang = iota
)

func isKeyWord(arg string) bool {
func IsKeyWord(arg string) bool {
switch arg {
case "break":
case "case":
Expand Down Expand Up @@ -165,7 +165,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 Down Expand Up @@ -209,7 +209,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 Down Expand Up @@ -298,6 +298,7 @@ func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[s

funcs := map[string]interface{}{
"bindtype": bindType[lang],
"bindtypenew": bindTypeNew[lang],
"bindtopictype": bindTopicType[lang],
"namedtype": namedType[lang],
"capitalise": capitalise,
Expand Down Expand Up @@ -328,6 +329,10 @@ var bindType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) stri
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 @@ -352,6 +357,39 @@ func bindBasicTypeGo(kind abi.Type) string {
}
}

// bindNewTypeNewGo 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).
Expand Down
55 changes: 44 additions & 11 deletions accounts/abi/bind/precompilebind/precompile_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ package precompilebind
import (
"errors"
"fmt"
"strings"

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

Expand All @@ -55,14 +57,20 @@ type BindedFiles struct {
}

// PrecompileBind generates a Go binding for a precompiled contract. It returns config binding and contract binding.
func PrecompileBind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang bind.Lang, libs map[string]string, aliases map[string]string, abifilename string, generateTests bool) (BindedFiles, error) {
func PrecompileBind(types []string, abiData string, bytecodes []string, fsigs []map[string]string, pkg string, lang bind.Lang, libs map[string]string, aliases map[string]string, abifilename string, generateTests bool) (BindedFiles, error) {
// create hooks
configHook := createPrecompileHook(abifilename, tmplSourcePrecompileConfigGo)
contractHook := createPrecompileHook(abifilename, tmplSourcePrecompileContractGo)
moduleHook := createPrecompileHook(abifilename, tmplSourcePrecompileModuleGo)
configTestHook := createPrecompileHook(abifilename, tmplSourcePrecompileConfigTestGo)
contractTestHook := createPrecompileHook(abifilename, tmplSourcePrecompileContractTestGo)

if err := verifyABI(abiData); err != nil {
return BindedFiles{}, err
}

abis := []string{abiData}

configBind, err := bind.BindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, configHook)
if err != nil {
return BindedFiles{}, fmt.Errorf("failed to generate config binding: %w", err)
Expand Down Expand Up @@ -114,16 +122,10 @@ func createPrecompileHook(abifilename string, template string) bind.BindHook {
contract := contracts[types[0]]

for k, v := range contract.Transacts {
if err := checkOutputName(*v); err != nil {
return nil, "", err
}
funcs[k] = v
}

for k, v := range contract.Calls {
if err := checkOutputName(*v); err != nil {
return nil, "", err
}
funcs[k] = v
}
isAllowList := allowListEnabled(funcs)
Expand Down Expand Up @@ -163,11 +165,42 @@ func allowListEnabled(funcs map[string]*bind.TmplMethod) bool {
return true
}

func checkOutputName(method bind.TmplMethod) error {
for _, output := range method.Original.Outputs {
if output.Name == "" {
return fmt.Errorf("ABI outputs for %s require a name to generate the precompile binding, re-generate the ABI from a Solidity source file with all named outputs", method.Original.Name)
func verifyABI(abiData string) error {
// check abi first
evmABI, err := abi.JSON(strings.NewReader(abiData))
if err != nil {
return err
}
if len(evmABI.Methods) == 0 {
return errors.New("no ABI methods found")
}
for _, method := range evmABI.Methods {
names := make(map[string]bool)
for _, input := range method.Inputs {
if bind.IsKeyWord(input.Name) {
return fmt.Errorf("input name %s is a keyword", input.Name)
}
name := abi.ToCamelCase(input.Name)
if names[name] {
return fmt.Errorf("normalized input name is duplicated: %s", name)
}
names[name] = true
}
names = make(map[string]bool)
for _, output := range method.Outputs {
if output.Name == "" {
return fmt.Errorf("ABI outputs for %s require a name to generate the precompile binding, re-generate the ABI from a Solidity source file with all named outputs", method.Name)
}
if bind.IsKeyWord(output.Name) {
return fmt.Errorf("output name %s is a keyword", output.Name)
}
name := abi.ToCamelCase(output.Name)
if names[name] {
return fmt.Errorf("normalized output name is duplicated: %s", name)
}
names[name] = true
}
}

return nil
}
Loading