From a4af734328d50b9ea89405c7e5050065a8087946 Mon Sep 17 00:00:00 2001
From: Matt K <1036969+mkrump@users.noreply.github.com>
Date: Sat, 29 Dec 2018 03:32:58 -0700
Subject: accounts/abi: change unpacking of abi fields w/ underscores (#16513)

* accounts/abi: fix name styling when unpacking abi fields w/ underscores

ABI fields with underscores that are being unpacked
into structs expect structs with following form:

int_one -> Int_one

whereas in abigen the generated structs are camelcased

int_one -> IntOne

so updated the unpack method to expect camelcased structs as well.
---
 accounts/abi/argument.go    | 17 ++++++++--------
 accounts/abi/bind/bind.go   | 41 ++++-----------------------------------
 accounts/abi/reflect.go     |  2 +-
 accounts/abi/unpack_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 60 insertions(+), 47 deletions(-)

(limited to 'accounts')

diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index 90fd9d05f..fdc6ff164 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -272,14 +272,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
 	return ret, nil
 }
 
-// capitalise makes the first character of a string upper case, also removing any
-// prefixing underscores from the variable names.
-func capitalise(input string) string {
-	for len(input) > 0 && input[0] == '_' {
-		input = input[1:]
-	}
-	if len(input) == 0 {
-		return ""
+// ToCamelCase converts an under-score string to a camel-case string
+func ToCamelCase(input string) string {
+	parts := strings.Split(input, "_")
+	for i, s := range parts {
+		if len(s) > 0 {
+			parts[i] = strings.ToUpper(s[:1]) + s[1:]
+		}
 	}
-	return strings.ToUpper(input[:1]) + input[1:]
+	return strings.Join(parts, "")
 }
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index 4dca4b4ea..21e16060c 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -387,48 +387,15 @@ var methodNormalizer = map[Lang]func(string) string{
 
 // capitalise makes a camel-case string which starts with an upper case character.
 func capitalise(input string) string {
-	for len(input) > 0 && input[0] == '_' {
-		input = input[1:]
-	}
-	if len(input) == 0 {
-		return ""
-	}
-	return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
+	return abi.ToCamelCase(input)
 }
 
 // decapitalise makes a camel-case string which starts with a lower case character.
 func decapitalise(input string) string {
-	for len(input) > 0 && input[0] == '_' {
-		input = input[1:]
-	}
-	if len(input) == 0 {
-		return ""
-	}
-	return toCamelCase(strings.ToLower(input[:1]) + input[1:])
-}
-
-// toCamelCase converts an under-score string to a camel-case string
-func toCamelCase(input string) string {
-	toupper := false
+	// NOTE: This is the current behavior, it doesn't match the comment
+	// above and needs to be investigated.
+	return abi.ToCamelCase(input)
 
-	result := ""
-	for k, v := range input {
-		switch {
-		case k == 0:
-			result = strings.ToUpper(string(input[0]))
-
-		case toupper:
-			result += strings.ToUpper(string(v))
-			toupper = false
-
-		case v == '_':
-			toupper = true
-
-		default:
-			result += string(v)
-		}
-	}
-	return result
 }
 
 // structured checks whether a list of ABI data types has enough information to
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index 6ec79a12f..f541cf3bf 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -186,7 +186,7 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
 	for _, arg := range args {
 
 		abiFieldName := arg.Name
-		structFieldName := capitalise(abiFieldName)
+		structFieldName := ToCamelCase(abiFieldName)
 
 		if structFieldName == "" {
 			return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index 22aa1a857..3873414c9 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -310,6 +310,53 @@ var unpackTests = []unpackTest{
 			Int2 *big.Int
 		}{big.NewInt(1), big.NewInt(2)},
 	},
+	{
+		def: `[{"name":"int_one","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			IntOne *big.Int
+		}{big.NewInt(1)},
+	},
+	{
+		def: `[{"name":"int__one","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			IntOne *big.Int
+		}{big.NewInt(1)},
+	},
+	{
+		def: `[{"name":"int_one_","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			IntOne *big.Int
+		}{big.NewInt(1)},
+	},
+	{
+		def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			IntOne *big.Int
+			Intone *big.Int
+		}{big.NewInt(1), big.NewInt(2)},
+	},
+	{
+		def: `[{"name":"___","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			IntOne *big.Int
+			Intone *big.Int
+		}{},
+		err: "abi: purely underscored output cannot unpack to struct",
+	},
+	{
+		def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
+		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+		want: struct {
+			Int1 *big.Int
+			Int2 *big.Int
+		}{},
+		err: "abi: multiple outputs mapping to the same struct field 'IntOne'",
+	},
 	{
 		def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`,
 		enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
-- 
cgit v1.2.3