mirror of https://github.com/status-im/op-geth.git
accounts/abi/bind: link dependent libs in deploy (#19718)
* accounts, abigen: link dependent libs in deploy * abigen: add java generation * bind: Fix unit tests * abigen: add unit test * Fix CI * Post-rebase fixes * Fix rebase issue * accounts/abi: Gary's review feedback * accounts/abi: More Gary feedback * accounts/abi: minor fixes
This commit is contained in:
parent
f2eb3b1c56
commit
5bc9ccfa0a
|
@ -31,6 +31,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lang is a target programming language selector to generate bindings for.
|
// Lang is a target programming language selector to generate bindings for.
|
||||||
|
@ -46,10 +47,13 @@ const (
|
||||||
// to be used as is in client code, but rather as an intermediate struct which
|
// to be used as is in client code, but rather as an intermediate struct which
|
||||||
// enforces compile time type safety and naming convention opposed to having to
|
// enforces compile time type safety and naming convention opposed to having to
|
||||||
// manually maintain hard coded strings that break on runtime.
|
// manually maintain hard coded strings that break on runtime.
|
||||||
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang) (string, error) {
|
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
|
||||||
// Process each individual contract requested binding
|
// Process each individual contract requested binding
|
||||||
contracts := make(map[string]*tmplContract)
|
contracts := make(map[string]*tmplContract)
|
||||||
|
|
||||||
|
// Map used to flag each encountered library as such
|
||||||
|
isLib := make(map[string]struct{})
|
||||||
|
|
||||||
for i := 0; i < len(types); i++ {
|
for i := 0; i < len(types); i++ {
|
||||||
// Parse the actual ABI to generate the binding for
|
// Parse the actual ABI to generate the binding for
|
||||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||||
|
@ -137,21 +141,44 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
contracts[types[i]] = &tmplContract{
|
contracts[types[i]] = &tmplContract{
|
||||||
Type: capitalise(types[i]),
|
Type: capitalise(types[i]),
|
||||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||||
InputBin: strings.TrimSpace(bytecodes[i]),
|
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
||||||
Constructor: evmABI.Constructor,
|
Constructor: evmABI.Constructor,
|
||||||
Calls: calls,
|
Calls: calls,
|
||||||
Transacts: transacts,
|
Transacts: transacts,
|
||||||
Events: events,
|
Events: events,
|
||||||
|
Libraries: make(map[string]string),
|
||||||
Structs: structs,
|
Structs: structs,
|
||||||
}
|
}
|
||||||
|
// Function 4-byte signatures are stored in the same sequence
|
||||||
|
// as types, if available.
|
||||||
if len(fsigs) > i {
|
if len(fsigs) > i {
|
||||||
contracts[types[i]].FuncSigs = fsigs[i]
|
contracts[types[i]].FuncSigs = fsigs[i]
|
||||||
}
|
}
|
||||||
|
// Parse library references.
|
||||||
|
for pattern, name := range libs {
|
||||||
|
matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
contracts[types[i]].Libraries[pattern] = name
|
||||||
|
// keep track that this type is a library
|
||||||
|
if _, ok := isLib[name]; !ok {
|
||||||
|
isLib[name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if that type has already been identified as a library
|
||||||
|
for i := 0; i < len(types); i++ {
|
||||||
|
_, ok := isLib[types[i]]
|
||||||
|
contracts[types[i]].Library = ok
|
||||||
}
|
}
|
||||||
// Generate the contract template data content and render it
|
// Generate the contract template data content and render it
|
||||||
data := &tmplData{
|
data := &tmplData{
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
Contracts: contracts,
|
Contracts: contracts,
|
||||||
|
Libraries: libs,
|
||||||
}
|
}
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -22,6 +22,7 @@ import "github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
type tmplData struct {
|
type tmplData struct {
|
||||||
Package string // Name of the package to place the generated file in
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplContract contains the data needed to generate an individual contract binding.
|
// tmplContract contains the data needed to generate an individual contract binding.
|
||||||
|
@ -34,7 +35,9 @@ type tmplContract struct {
|
||||||
Calls map[string]*tmplMethod // Contract calls that only read state data
|
Calls map[string]*tmplMethod // Contract calls that only read state data
|
||||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||||
Events map[string]*tmplEvent // Contract events accessors
|
Events map[string]*tmplEvent // Contract events accessors
|
||||||
|
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
||||||
Structs map[string]*tmplStruct // Contract struct type definitions
|
Structs map[string]*tmplStruct // Contract struct type definitions
|
||||||
|
Library bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -113,15 +116,14 @@ var (
|
||||||
{{if $contract.FuncSigs}}
|
{{if $contract.FuncSigs}}
|
||||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
||||||
var {{.Type}}FuncSigs = map[string]string{
|
var {{.Type}}FuncSigs = map[string]string{
|
||||||
{{range $strsig, $binsig := .FuncSigs}}
|
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
||||||
"{{$binsig}}": "{{$strsig}}",
|
|
||||||
{{end}}
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .InputBin}}
|
{{if .InputBin}}
|
||||||
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
||||||
const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + `
|
var {{.Type}}Bin = "0x{{.InputBin}}"
|
||||||
|
|
||||||
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||||
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
||||||
|
@ -129,6 +131,10 @@ var (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
|
{{range $pattern, $name := .Libraries}}
|
||||||
|
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||||
|
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
||||||
|
{{end}}
|
||||||
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
|
@ -503,7 +509,7 @@ import java.util.*;
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
{{$structs := $contract.Structs}}
|
{{$structs := $contract.Structs}}
|
||||||
public class {{.Type}} {
|
{{if not .Library}}public {{end}}class {{.Type}} {
|
||||||
// ABI is the input ABI used to generate the binding from.
|
// ABI is the input ABI used to generate the binding from.
|
||||||
public final static String ABI = "{{.InputABI}}";
|
public final static String ABI = "{{.InputABI}}";
|
||||||
{{if $contract.FuncSigs}}
|
{{if $contract.FuncSigs}}
|
||||||
|
@ -511,8 +517,7 @@ public class {{.Type}} {
|
||||||
public final static Map<String, String> {{.Type}}FuncSigs;
|
public final static Map<String, String> {{.Type}}FuncSigs;
|
||||||
static {
|
static {
|
||||||
Hashtable<String, String> temp = new Hashtable<String, String>();
|
Hashtable<String, String> temp = new Hashtable<String, String>();
|
||||||
{{range $strsig, $binsig := .FuncSigs}}
|
{{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
|
||||||
temp.put("{{$binsig}}", "{{$strsig}}");
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
|
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
|
||||||
}
|
}
|
||||||
|
@ -524,9 +529,18 @@ public class {{.Type}} {
|
||||||
// deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
// deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||||
public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
||||||
Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
|
Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
|
||||||
|
String bytecode = BYTECODE;
|
||||||
|
{{if .Libraries}}
|
||||||
|
|
||||||
|
// "link" contract to dependent libraries by deploying them first.
|
||||||
|
{{range $pattern, $name := .Libraries}}
|
||||||
|
{{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
|
||||||
|
bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
{{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
{{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
||||||
{{end}}
|
{{end}}
|
||||||
return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(BYTECODE), client, args));
|
return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal constructor used by contract deployment.
|
// Internal constructor used by contract deployment.
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common/compiler"
|
"github.com/ethereum/go-ethereum/common/compiler"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -81,6 +82,7 @@ func main() {
|
||||||
bins []string
|
bins []string
|
||||||
types []string
|
types []string
|
||||||
sigs []map[string]string
|
sigs []map[string]string
|
||||||
|
libs = make(map[string]string)
|
||||||
)
|
)
|
||||||
if *solFlag != "" || *vyFlag != "" || *abiFlag == "-" {
|
if *solFlag != "" || *vyFlag != "" || *abiFlag == "-" {
|
||||||
// Generate the list of types to exclude from binding
|
// Generate the list of types to exclude from binding
|
||||||
|
@ -128,6 +130,9 @@ func main() {
|
||||||
|
|
||||||
nameParts := strings.Split(name, ":")
|
nameParts := strings.Split(name, ":")
|
||||||
types = append(types, nameParts[len(nameParts)-1])
|
types = append(types, nameParts[len(nameParts)-1])
|
||||||
|
|
||||||
|
libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
|
||||||
|
libs[libPattern] = nameParts[len(nameParts)-1]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise load up the ABI, optional bytecode and type name from the parameters
|
// Otherwise load up the ABI, optional bytecode and type name from the parameters
|
||||||
|
@ -155,7 +160,7 @@ func main() {
|
||||||
types = append(types, kind)
|
types = append(types, kind)
|
||||||
}
|
}
|
||||||
// Generate the contract binding
|
// Generate the contract binding
|
||||||
code, err := bind.Bind(types, abis, bins, sigs, *pkgFlag, lang)
|
code, err := bind.Bind(types, abis, bins, sigs, *pkgFlag, lang, libs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to generate ABI binding: %v\n", err)
|
fmt.Printf("Failed to generate ABI binding: %v\n", err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
|
|
Loading…
Reference in New Issue