From 0d6bc27fb761f7e4ec975c1c301d16e623ba8791 Mon Sep 17 00:00:00 2001 From: gary rong Date: Mon, 25 Nov 2019 21:03:22 +0800 Subject: [PATCH] accounts/abi/bind: avoid reclaring structs (#20381) --- accounts/abi/bind/bind.go | 15 ++++--- accounts/abi/bind/bind_test.go | 82 ++++++++++++++++++++++++++++++++++ accounts/abi/bind/template.go | 24 +++++----- 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index e51b44563a17..13ac286b4552 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -48,12 +48,16 @@ const ( // enforces compile time type safety and naming convention opposed to having to // 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, libs map[string]string, aliases map[string]string) (string, error) { - // Process each individual contract requested binding - contracts := make(map[string]*tmplContract) + var ( + // contracts is the map of each individual contract requested binding + contracts = make(map[string]*tmplContract) - // Map used to flag each encountered library as such - isLib := make(map[string]struct{}) + // structs is the map of all reclared structs shared by passed contracts. + structs = make(map[string]*tmplStruct) + // isLib is the map used to flag each encountered library as such + isLib = make(map[string]struct{}) + ) for i := 0; i < len(types); i++ { // Parse the actual ABI to generate the binding for evmABI, err := abi.JSON(strings.NewReader(abis[i])) @@ -73,7 +77,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] calls = make(map[string]*tmplMethod) transacts = make(map[string]*tmplMethod) events = make(map[string]*tmplEvent) - structs = make(map[string]*tmplStruct) // identifiers are used to detect duplicated identifier of function // and event. For all calls, transacts and events, abigen will generate @@ -168,7 +171,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] Transacts: transacts, Events: events, Libraries: make(map[string]string), - Structs: structs, } // Function 4-byte signatures are stored in the same sequence // as types, if available. @@ -200,6 +202,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] Package: pkg, Contracts: contracts, Libraries: libs, + Structs: structs, } buffer := new(bytes.Buffer) diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index e0b282494bdf..7594af775e8b 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -1448,6 +1448,88 @@ var bindTests = []struct { map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar nil, }, + { + "MultiContracts", + ` + pragma solidity ^0.5.11; + pragma experimental ABIEncoderV2; + + library ExternalLib { + struct SharedStruct{ + uint256 f1; + bytes32 f2; + } + } + + contract ContractOne { + function foo(ExternalLib.SharedStruct memory s) pure public { + // Do stuff + } + } + + contract ContractTwo { + function bar(ExternalLib.SharedStruct memory s) pure public { + // Do stuff + } + } + `, + []string{ + `60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c80639d8a8ba81461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a72315820749274eb7f6c01010d5322af4e1668b0a154409eb7968bd6cae5524c7ed669bb6c6578706572696d656e74616cf564736f6c634300050c0040`, + `60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c8063db8ba08c1461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a723158209bc28ee7ea97c131a13330d77ec73b4493b5c59c648352da81dd288b021192596c6578706572696d656e74616cf564736f6c634300050c0040`, + `606c6026600b82828239805160001a6073141515601857fe5b30600052607381538281f350fe73000000000000000000000000000000000000000030146080604052600436106023575b60006000fdfea365627a7a72315820518f0110144f5b3de95697d05e456a064656890d08e6f9cff47f3be710cc46a36c6578706572696d656e74616cf564736f6c634300050c0040`, + }, + []string{ + `[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`, + `[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"bar","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`, + `[]`, + }, + ` + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/core" + `, + ` + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Deploy registrar contract + sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000) + defer sim.Close() + + transactOpts := bind.NewKeyedTransactor(key) + _, _, c1, err := DeployContractOne(transactOpts, sim) + if err != nil { + t.Fatal("Failed to deploy contract") + } + sim.Commit() + err = c1.Foo(nil, ExternalLibSharedStruct{ + F1: big.NewInt(100), + F2: [32]byte{0x01, 0x02, 0x03}, + }) + if err != nil { + t.Fatal("Failed to invoke function") + } + _, _, c2, err := DeployContractTwo(transactOpts, sim) + if err != nil { + t.Fatal("Failed to deploy contract") + } + sim.Commit() + err = c2.Bar(nil, ExternalLibSharedStruct{ + F1: big.NewInt(100), + F2: [32]byte{0x01, 0x02, 0x03}, + }) + if err != nil { + t.Fatal("Failed to invoke function") + } + `, + nil, + nil, + nil, + []string{"ContractOne", "ContractTwo", "ExternalLib"}, + }, } // Tests that packages generated by the binder can be successfully compiled and diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 5b35b1badcc2..c96dd1b9955d 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -23,6 +23,7 @@ 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 Libraries map[string]string // Map the bytecode's link pattern to the library name + Structs map[string]*tmplStruct // Contract struct type definitions } // tmplContract contains the data needed to generate an individual contract binding. @@ -36,8 +37,7 @@ type tmplContract struct { Transacts map[string]*tmplMethod // Contract calls that write state data 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 - Library bool + Library bool // Indicator whether the contract is a library } // tmplMethod is a wrapper around an abi.Method that contains a few preprocessed @@ -108,8 +108,16 @@ var ( _ = event.NewSubscription ) +{{$structs := .Structs}} +{{range $structs}} + // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. + type {{.Name}} struct { + {{range $field := .Fields}} + {{$field.Name}} {{$field.Type}}{{end}} + } +{{end}} + {{range $contract := .Contracts}} - {{$structs := $contract.Structs}} // {{.Type}}ABI is the input ABI used to generate the binding from. const {{.Type}}ABI = "{{.InputABI}}" @@ -285,14 +293,6 @@ var ( return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...) } - {{range .Structs}} - // {{.Name}} is an auto generated low-level Go binding around an user-defined struct. - type {{.Name}} struct { - {{range $field := .Fields}} - {{$field.Name}} {{$field.Type}}{{end}} - } - {{end}} - {{range .Calls}} // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. // @@ -507,8 +507,8 @@ package {{.Package}}; import org.ethereum.geth.*; import java.util.*; +{{$structs := .Structs}} {{range $contract := .Contracts}} -{{$structs := $contract.Structs}} {{if not .Library}}public {{end}}class {{.Type}} { // ABI is the input ABI used to generate the binding from. public final static String ABI = "{{.InputABI}}";