From d41cfec6284b3e73bfaa2d0d63fcbd1ce26b4bc2 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 5 Nov 2019 19:11:03 +0800 Subject: [PATCH] accounts/abi/bind, cmd/abigen: implement alias for abigen --- accounts/abi/bind/bind.go | 19 +++++++-- accounts/abi/bind/bind_test.go | 70 +++++++++++++++++++++++++++++++++- cmd/abigen/main.go | 38 +++++++++++++++--- 3 files changed, 116 insertions(+), 11 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 7bda997a61..4de1b623de 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -47,7 +47,7 @@ const ( // 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 // 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) (string, error) { +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) @@ -78,7 +78,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] for _, original := range evmABI.Methods { // Normalize the method for capital cases and non-anonymous inputs/outputs normalized := original - normalized.Name = methodNormalizer[lang](original.Name) + normalized.Name = methodNormalizer[lang](alias(aliases, original.Name)) normalized.Inputs = make([]abi.Argument, len(original.Inputs)) copy(normalized.Inputs, original.Inputs) @@ -114,7 +114,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] } // Normalize the event for capital cases and non-anonymous outputs normalized := original - normalized.Name = methodNormalizer[lang](original.Name) + normalized.Name = methodNormalizer[lang](alias(aliases, original.Name)) normalized.Inputs = make([]abi.Argument, len(original.Inputs)) copy(normalized.Inputs, original.Inputs) @@ -483,6 +483,19 @@ func namedTypeJava(javaKind string, solKind abi.Type) string { } } +// alias returns an alias of the given string based on the aliasing rules +// or returns itself if no rule is matched. +func alias(aliases map[string]string, n string) string { + // Short circuit if aliasing rule is empty + if aliases == nil { + return n + } + if alias, exist := aliases[n]; exist { + return alias + } + return n +} + // methodNormalizer is a name transformer that modifies Solidity method names to // conform to target language naming concentions. var methodNormalizer = map[Lang]func(string) string{ diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 1db5682833..614f272029 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -38,6 +38,7 @@ var bindTests = []struct { tester string fsigs []map[string]string libs map[string]string + aliases map[string]string types []string }{ // Test that the binding is available in combined and separate forms too @@ -61,6 +62,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Test that all the official sample contracts bind correctly { @@ -77,6 +79,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, { `Crowdsale`, @@ -92,6 +95,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, { `DAO`, @@ -107,6 +111,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Test that named and anonymous inputs are handled correctly { @@ -143,6 +148,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Test that named and anonymous outputs are handled correctly { @@ -182,6 +188,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that named, anonymous and indexed events are handled correctly { @@ -250,6 +257,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Test that contract interactions (deploy, transact and call) generate working code { @@ -311,6 +319,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that plain values can be properly returned and deserialized { @@ -356,6 +365,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that tuples can be properly returned and deserialized { @@ -401,6 +411,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that arrays/slices can be properly returned and deserialized. // Only addresses are tested, remainder just compiled to keep the test small. @@ -458,6 +469,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that anonymous default methods can be correctly invoked { @@ -508,6 +520,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that non-existent contracts are reported as such (though only simulator test) { @@ -547,6 +560,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that gas estimation works for contracts with weird gas mechanics too. { @@ -602,6 +616,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Test that constant functions can be called from an (optional) specified address { @@ -655,6 +670,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that methods and returns with underscores inside work correctly. { @@ -734,6 +750,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, // Tests that logs can be successfully filtered and decoded. { @@ -955,6 +972,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, { `DeeplyNestedArray`, @@ -1035,6 +1053,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, { `CallbackParam`, @@ -1076,6 +1095,7 @@ var bindTests = []struct { }, nil, nil, + nil, }, { `Tuple`, ` @@ -1219,6 +1239,7 @@ var bindTests = []struct { nil, nil, nil, + nil, }, { `UseLibrary`, @@ -1287,6 +1308,7 @@ var bindTests = []struct { map[string]string{ "b98c933f0a6ececcd167bd4f9d3299b1a0": "Math", }, + nil, []string{"UseLibrary", "Math"}, }, { "Overload", @@ -1381,6 +1403,50 @@ var bindTests = []struct { nil, nil, nil, + nil, + }, + { + "IdentifierCollision", + ` + pragma solidity >=0.4.19 <0.6.0; + + contract IdentifierCollision { + uint public _myVar; + + function MyVar() public view returns (uint) { + return _myVar; + } + } + `, + []string{"60806040523480156100115760006000fd5b50610017565b60c3806100256000396000f3fe608060405234801560105760006000fd5b506004361060365760003560e01c806301ad4d8714603c5780634ef1f0ad146058576036565b60006000fd5b60426074565b6040518082815260200191505060405180910390f35b605e607d565b6040518082815260200191505060405180910390f35b60006000505481565b60006000600050549050608b565b9056fea265627a7a7231582067c8d84688b01c4754ba40a2a871cede94ea1f28b5981593ab2a45b46ac43af664736f6c634300050c0032"}, + []string{`[{"constant":true,"inputs":[],"name":"MyVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_myVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","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" + `, + ` + // Initialize test accounts + 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) + _, _, _, err := DeployIdentifierCollision(transactOpts, sim) + if err != nil { + t.Fatalf("failed to deploy contract: %v", err) + } + `, + nil, + nil, + map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar + nil, }, } @@ -1412,7 +1478,7 @@ func TestGolangBindings(t *testing.T) { types = []string{tt.name} } // Generate the binding and create a Go source file in the workspace - bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs) + bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } @@ -1835,7 +1901,7 @@ public class Test { }, } for i, c := range cases { - binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, nil, "bindtest", LangJava, nil) + binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, nil, "bindtest", LangJava, nil, nil) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) } diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 7698115008..0efd3f880c 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -103,6 +103,10 @@ var ( Usage: "Destination language for the bindings (go, java, objc)", Value: "go", } + aliasFlag = cli.StringFlag{ + Name: "alias", + Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar", + } ) func init() { @@ -120,6 +124,7 @@ func init() { pkgFlag, outFlag, langFlag, + aliasFlag, } app.Action = utils.MigrateFlags(abigen) cli.CommandHelpTemplate = commandHelperTemplate @@ -144,11 +149,12 @@ func abigen(c *cli.Context) error { } // If the entire solidity code was specified, build and bind based on that var ( - abis []string - bins []string - types []string - sigs []map[string]string - libs = make(map[string]string) + abis []string + bins []string + types []string + sigs []map[string]string + libs = make(map[string]string) + aliases = make(map[string]string) ) if c.GlobalString(abiFlag.Name) != "" { // Load up the ABI, optional bytecode and type name from the parameters @@ -232,8 +238,28 @@ func abigen(c *cli.Context) error { libs[libPattern] = nameParts[len(nameParts)-1] } } + // Extract all aliases from the flags + if c.GlobalIsSet(aliasFlag.Name) { + v := strings.Split(c.GlobalString(aliasFlag.Name), ",") + for _, alias := range v { + // We support multi-versions for aliasing + // e.g. + // foo=bar + // foo:bar + index := strings.Index(alias, ":") + if index == -1 { + index = strings.Index(alias, "=") + } + // Filter out invalid aliasing + if index == -1 || index == 0 || index == len(alias)-1 { + continue + } + aliases[alias[:index]] = alias[index+1:] // Add to the aliasing map + } + } + // Generate the contract binding - code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs) + code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases) if err != nil { utils.Fatalf("Failed to generate ABI binding: %v", err) }