From 5d5f243a217f48b92d6a6ee09124ebf8fbe3cb79 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Mar 2023 12:08:01 +0200 Subject: [PATCH 1/8] accounts/abi: resolve name conflict for methods starting with a number --- accounts/abi/utils.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index b1537ca58dd3..1ef24afee064 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -16,7 +16,10 @@ package abi -import "fmt" +import ( + "fmt" + "unicode" +) // ResolveNameConflict returns the next available name for a given thing. // This helper can be used for lots of purposes: @@ -29,8 +32,12 @@ import "fmt" // // Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains // Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send". +// If a method name starts with a number an m is prepended (e.g. 1method -> m1method). func ResolveNameConflict(rawName string, used func(string) bool) string { name := rawName + if unicode.IsDigit(rune(name[0])) { + name = fmt.Sprintf("%v%v", "m", name) + } ok := used(name) for idx := 0; ok; idx++ { name = fmt.Sprintf("%s%d", rawName, idx) From 9f175c630cd61594bf54e975760a285475643b16 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Mar 2023 12:13:10 +0200 Subject: [PATCH 2/8] accounts/abi: strings > values --- accounts/abi/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index 1ef24afee064..45d1a5c9ac9b 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -36,7 +36,7 @@ import ( func ResolveNameConflict(rawName string, used func(string) bool) string { name := rawName if unicode.IsDigit(rune(name[0])) { - name = fmt.Sprintf("%v%v", "m", name) + name = fmt.Sprintf("%s%s", "m", name) } ok := used(name) for idx := 0; ok; idx++ { From 7da4285d23411cf8afa8e2fdd5029ae747a99183 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 28 Mar 2023 13:20:51 +0200 Subject: [PATCH 3/8] accounts/abi: fix conflict resolution, add tests --- accounts/abi/utils.go | 9 +++--- accounts/abi/utils_test.go | 61 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 accounts/abi/utils_test.go diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index 45d1a5c9ac9b..8b106af84cc8 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -34,13 +34,14 @@ import ( // Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send". // If a method name starts with a number an m is prepended (e.g. 1method -> m1method). func ResolveNameConflict(rawName string, used func(string) bool) string { - name := rawName - if unicode.IsDigit(rune(name[0])) { - name = fmt.Sprintf("%s%s", "m", name) + prefixedName := rawName + if unicode.IsDigit(rune(rawName[0])) { + prefixedName = fmt.Sprintf("%s%s", "m", rawName) } + name := prefixedName ok := used(name) for idx := 0; ok; idx++ { - name = fmt.Sprintf("%s%d", rawName, idx) + name = fmt.Sprintf("%s%d", prefixedName, idx) ok = used(name) } return name diff --git a/accounts/abi/utils_test.go b/accounts/abi/utils_test.go new file mode 100644 index 000000000000..04e49b7a09f4 --- /dev/null +++ b/accounts/abi/utils_test.go @@ -0,0 +1,61 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package abi + +import "testing" + +func TestResolveNameConflict(t *testing.T) { + db := make(map[string]struct{}) + used := func(s string) bool { + _, ok := db[s] + return ok + } + + var tests = []struct { + input string + output string + }{ + { + input: "1method", + output: "m1method", + }, + { + input: "1method", + output: "m1method0", + }, + { + input: "1method", + output: "m1method1", + }, + { + input: "method", + output: "method", + }, + { + input: "method", + output: "method0", + }, + } + + for _, test := range tests { + result := ResolveNameConflict(test.input, used) + if result != test.output { + t.Errorf("resolving name conflict failed, got %v want %v input %v", result, test.output, test.input) + } + db[result] = struct{}{} + } +} From 9f91e002bd277902387ab92d694d2dd23b8fb110 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 29 Mar 2023 11:31:23 +0200 Subject: [PATCH 4/8] Update accounts/abi/utils.go Co-authored-by: Martin Holst Swende --- accounts/abi/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index 8b106af84cc8..134a3e5d593c 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -35,7 +35,7 @@ import ( // If a method name starts with a number an m is prepended (e.g. 1method -> m1method). func ResolveNameConflict(rawName string, used func(string) bool) string { prefixedName := rawName - if unicode.IsDigit(rune(rawName[0])) { + if len(rawName) > 0 && unicode.IsDigit(rune(rawName[0])) { prefixedName = fmt.Sprintf("%s%s", "m", rawName) } name := prefixedName From 8507eca0b47b363a0eafd45170622d5084fb621b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 29 Mar 2023 11:44:32 +0200 Subject: [PATCH 5/8] accounts/abi: add test --- accounts/abi/utils_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/accounts/abi/utils_test.go b/accounts/abi/utils_test.go index 04e49b7a09f4..b7538ebd6d4f 100644 --- a/accounts/abi/utils_test.go +++ b/accounts/abi/utils_test.go @@ -49,6 +49,10 @@ func TestResolveNameConflict(t *testing.T) { input: "method", output: "method0", }, + { + input: "", + output: "", + }, } for _, test := range tests { From a5dc1ea37c3a6328d47be1091cffe9df0d3930a6 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 31 Mar 2023 10:48:57 +0200 Subject: [PATCH 6/8] accounts/abi: capitalize prefix --- accounts/abi/utils.go | 6 ++++-- accounts/abi/utils_test.go | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index 134a3e5d593c..75a4e0f3f219 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -32,11 +32,13 @@ import ( // // Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains // Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send". -// If a method name starts with a number an m is prepended (e.g. 1method -> m1method). +// If a method name starts with a number an M is prepended (e.g. 1method -> M1method). +// This will export the method in the ABI, which is okay since a leading number can be interpreted +// as an uppercase letter. func ResolveNameConflict(rawName string, used func(string) bool) string { prefixedName := rawName if len(rawName) > 0 && unicode.IsDigit(rune(rawName[0])) { - prefixedName = fmt.Sprintf("%s%s", "m", rawName) + prefixedName = fmt.Sprintf("%s%s", "M", rawName) } name := prefixedName ok := used(name) diff --git a/accounts/abi/utils_test.go b/accounts/abi/utils_test.go index b7538ebd6d4f..9c6b1fe8d4cb 100644 --- a/accounts/abi/utils_test.go +++ b/accounts/abi/utils_test.go @@ -31,15 +31,15 @@ func TestResolveNameConflict(t *testing.T) { }{ { input: "1method", - output: "m1method", + output: "M1method", }, { input: "1method", - output: "m1method0", + output: "M1method0", }, { input: "1method", - output: "m1method1", + output: "M1method1", }, { input: "method", From a2f34b809df27ce4f77af25a71a3760e69d805d6 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 24 Apr 2023 15:19:23 +0200 Subject: [PATCH 7/8] Check for names prefixed with _ --- accounts/abi/bind/bind.go | 17 ++++++++++++++++- accounts/abi/bind/bind_test.go | 23 +++++++++++++++++++++++ accounts/abi/utils.go | 12 ++---------- accounts/abi/utils_test.go | 12 ------------ 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 05cca8e90b3a..8a54a0e6ef04 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -133,12 +133,19 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] // Normalize the method for capital cases and non-anonymous inputs/outputs normalized := original normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) - // Ensure there is no duplicated identifier var identifiers = callIdentifiers if !original.IsConstant() { identifiers = transactIdentifiers } + // Name shouldn't start with a digit. It will make the generated code invalid. + if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { + normalizedName = fmt.Sprintf("M%s", normalizedName) + normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { + _, ok := identifiers[name] + return ok + }) + } if identifiers[normalizedName] { return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) } @@ -182,6 +189,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] // Ensure there is no duplicated identifier normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) + // Name shouldn't start with a digit. It will make the generated code invalid. + if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { + normalizedName = fmt.Sprintf("E%s", normalizedName) + normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { + _, ok := eventIdentifiers[name] + return ok + }) + } if eventIdentifiers[normalizedName] { return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index cbbce7b30889..1069f3d396d4 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -2038,6 +2038,29 @@ var bindTests = []struct { t.Errorf("error deploying the contract: %v", err) } `, + }, { + name: "NumericMethodName", + contract: ` + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.4.22 <0.9.0; + + contract NumericMethodName { + event _1TestEvent(address _param); + function _1test() public pure {} + function __1test() public pure {} + function __2test() public pure {} + } + `, + bytecode: []string{"0x6080604052348015600f57600080fd5b5060958061001e6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80639d993132146041578063d02767c7146049578063ffa02795146051575b600080fd5b60476059565b005b604f605b565b005b6057605d565b005b565b565b56fea26469706673582212200382ca602dff96a7e2ba54657985e2b4ac423a56abe4a1f0667bc635c4d4371f64736f6c63430008110033"}, + abi: []string{`[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_param","type":"address"}],"name":"_1TestEvent","type":"event"},{"inputs":[],"name":"_1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__1test","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"__2test","outputs":[],"stateMutability":"pure","type":"function"}]`}, + imports: ` + "github.com/ethereum/go-ethereum/common" + `, + tester: ` + if b, err := NewNumericMethodName(common.Address{}, nil); b == nil || err != nil { + t.Fatalf("combined binding (%v) nil or error (%v) not nil", b, nil) + } +`, }, } diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index 75a4e0f3f219..b99acf2706f7 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -18,7 +18,6 @@ package abi import ( "fmt" - "unicode" ) // ResolveNameConflict returns the next available name for a given thing. @@ -32,18 +31,11 @@ import ( // // Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains // Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send". -// If a method name starts with a number an M is prepended (e.g. 1method -> M1method). -// This will export the method in the ABI, which is okay since a leading number can be interpreted -// as an uppercase letter. func ResolveNameConflict(rawName string, used func(string) bool) string { - prefixedName := rawName - if len(rawName) > 0 && unicode.IsDigit(rune(rawName[0])) { - prefixedName = fmt.Sprintf("%s%s", "M", rawName) - } - name := prefixedName + name := rawName ok := used(name) for idx := 0; ok; idx++ { - name = fmt.Sprintf("%s%d", prefixedName, idx) + name = fmt.Sprintf("%s%d", rawName, idx) ok = used(name) } return name diff --git a/accounts/abi/utils_test.go b/accounts/abi/utils_test.go index 9c6b1fe8d4cb..2b5780520afa 100644 --- a/accounts/abi/utils_test.go +++ b/accounts/abi/utils_test.go @@ -29,18 +29,6 @@ func TestResolveNameConflict(t *testing.T) { input string output string }{ - { - input: "1method", - output: "M1method", - }, - { - input: "1method", - output: "M1method0", - }, - { - input: "1method", - output: "M1method1", - }, { input: "method", output: "method", From cdd46fb8b03af1fdfd82510d3926819fbb0402d0 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 2 May 2023 11:02:46 +0200 Subject: [PATCH 8/8] accounts/abi: reduce diff --- accounts/abi/utils.go | 4 +-- accounts/abi/utils_test.go | 53 -------------------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 accounts/abi/utils_test.go diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go index b99acf2706f7..b1537ca58dd3 100644 --- a/accounts/abi/utils.go +++ b/accounts/abi/utils.go @@ -16,9 +16,7 @@ package abi -import ( - "fmt" -) +import "fmt" // ResolveNameConflict returns the next available name for a given thing. // This helper can be used for lots of purposes: diff --git a/accounts/abi/utils_test.go b/accounts/abi/utils_test.go deleted file mode 100644 index 2b5780520afa..000000000000 --- a/accounts/abi/utils_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package abi - -import "testing" - -func TestResolveNameConflict(t *testing.T) { - db := make(map[string]struct{}) - used := func(s string) bool { - _, ok := db[s] - return ok - } - - var tests = []struct { - input string - output string - }{ - { - input: "method", - output: "method", - }, - { - input: "method", - output: "method0", - }, - { - input: "", - output: "", - }, - } - - for _, test := range tests { - result := ResolveNameConflict(test.input, used) - if result != test.output { - t.Errorf("resolving name conflict failed, got %v want %v input %v", result, test.output, test.input) - } - db[result] = struct{}{} - } -}