From ec726db49dff5e59f4974ed7fe500fe7203387a6 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 6 Jan 2025 09:12:27 +0800 Subject: [PATCH] accounts/abi/bind: avoid reclaring structs (#20381) --- accounts/abi/bind/bind.go | 15 +++--- accounts/abi/bind/bind_test.go | 83 ++++++++++++++++++++++++++++++++++ accounts/abi/bind/template.go | 22 ++++----- 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 6a28ad0f7718..f7b5023f9dfd 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -45,12 +45,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])) @@ -70,7 +74,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 @@ -160,7 +163,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. @@ -192,6 +194,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 d6b35156ea92..22cd418dafe6 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -1153,6 +1153,89 @@ 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/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/params" + `, + ` + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Deploy registrar contract + sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig) + 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 3a0c7bf5a900..1c1a7255cd25 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 @@ -106,8 +106,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}}" @@ -283,14 +291,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}}. //