Skip to content

Commit

Permalink
accounts/abi/bind, cmd/abigen: implement alias for abigen (ethereum#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
gzliudan committed Jan 9, 2025
1 parent 4dc7c7a commit cebd532
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 11 deletions.
41 changes: 37 additions & 4 deletions accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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)

Expand All @@ -71,12 +71,29 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
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
// corresponding bindings. However we have to ensure there is no
// identifier coliision in the bindings of these categories.
callIdentifiers = make(map[string]bool)
transactIdentifiers = make(map[string]bool)
eventIdentifiers = make(map[string]bool)
)
for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs
normalized := original
normalized.Name = methodNormalizer[lang](original.Name)

normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
// Ensure there is no duplicated identifier
var identifiers = callIdentifiers
if !original.Const {
identifiers = transactIdentifiers
}
if identifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
identifiers[normalizedName] = true
normalized.Name = normalizedName
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
Expand Down Expand Up @@ -111,7 +128,14 @@ 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)

// Ensure there is no duplicated identifier
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
if eventIdentifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName

normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
Expand Down Expand Up @@ -316,6 +340,15 @@ var namedType = map[Lang]func(string, abi.Type) string{
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
}

// 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 {
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{
Expand Down
66 changes: 65 additions & 1 deletion accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -61,6 +62,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that all the official sample contracts bind correctly
{
Expand All @@ -77,6 +79,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`Crowdsale`,
Expand All @@ -92,6 +95,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`DAO`,
Expand All @@ -107,6 +111,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that named and anonymous inputs are handled correctly
{
Expand Down Expand Up @@ -143,6 +148,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that named and anonymous outputs are handled correctly
{
Expand Down Expand Up @@ -182,6 +188,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that named, anonymous and indexed events are handled correctly
{
Expand Down Expand Up @@ -250,6 +257,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that contract interactions (deploy, transact and call) generate working code
{
Expand Down Expand Up @@ -312,6 +320,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that plain values can be properly returned and deserialized
{
Expand Down Expand Up @@ -358,6 +367,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that tuples can be properly returned and deserialized
{
Expand Down Expand Up @@ -404,6 +414,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.
Expand Down Expand Up @@ -462,6 +473,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that anonymous default methods can be correctly invoked
{
Expand Down Expand Up @@ -513,6 +525,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that non-existent contracts are reported as such (though only simulator test)
{
Expand Down Expand Up @@ -552,6 +565,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that gas estimation works for contracts with weird gas mechanics too.
{
Expand Down Expand Up @@ -608,6 +622,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that constant functions can be called from an (optional) specified address
{
Expand Down Expand Up @@ -663,6 +678,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that methods and returns with underscores inside work correctly.
{
Expand Down Expand Up @@ -743,6 +759,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that logs can be successfully filtered and decoded.
{
Expand Down Expand Up @@ -965,6 +982,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`DeeplyNestedArray`,
Expand Down Expand Up @@ -1046,6 +1064,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`CallbackParam`,
Expand Down Expand Up @@ -1088,6 +1107,51 @@ var bindTests = []struct {
},
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/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"
`,
`
// Initialize test accounts
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)
_, _, _, 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,
},
}

Expand Down Expand Up @@ -1120,7 +1184,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)
}
Expand Down
31 changes: 25 additions & 6 deletions cmd/abigen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io"
"os"
"regexp"
"strings"

"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
Expand Down Expand Up @@ -74,6 +75,10 @@ var (
Usage: "Destination language for the bindings (go)",
Value: "go",
}
aliasFlag = &cli.StringFlag{
Name: "alias",
Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar",
}
)

func init() {
Expand All @@ -88,6 +93,7 @@ func init() {
pkgFlag,
outFlag,
langFlag,
aliasFlag,
}
app.Action = abigen
}
Expand All @@ -100,11 +106,12 @@ func abigen(c *cli.Context) error {
lang := bind.LangGo
// 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.String(abiFlag.Name) != "" {
// Load up the ABI, optional bytecode and type name from the parameters
Expand Down Expand Up @@ -176,8 +183,20 @@ func abigen(c *cli.Context) error {
libs[libPattern] = nameParts[len(nameParts)-1]
}
}
// Extract all aliases from the flags
if c.IsSet(aliasFlag.Name) {
// We support multi-versions for aliasing
// e.g.
// foo=bar,foo2=bar2
// foo:bar,foo2:bar2
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
for _, match := range submatches {
aliases[match[1]] = match[2]
}
}
// Generate the contract binding
code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs)
code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
if err != nil {
utils.Fatalf("Failed to generate ABI binding: %v", err)
}
Expand Down

0 comments on commit cebd532

Please sign in to comment.