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.
This commit is contained in:
Matt K 2018-12-29 03:32:58 -07:00 committed by Guillaume Ballet
parent 735343430d
commit a4af734328
4 changed files with 60 additions and 47 deletions

View File

@ -272,14 +272,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
return ret, nil return ret, nil
} }
// capitalise makes the first character of a string upper case, also removing any // ToCamelCase converts an under-score string to a camel-case string
// prefixing underscores from the variable names. func ToCamelCase(input string) string {
func capitalise(input string) string { parts := strings.Split(input, "_")
for len(input) > 0 && input[0] == '_' { for i, s := range parts {
input = input[1:] if len(s) > 0 {
parts[i] = strings.ToUpper(s[:1]) + s[1:]
}
} }
if len(input) == 0 { return strings.Join(parts, "")
return ""
}
return strings.ToUpper(input[:1]) + input[1:]
} }

View File

@ -387,48 +387,15 @@ var methodNormalizer = map[Lang]func(string) string{
// capitalise makes a camel-case string which starts with an upper case character. // capitalise makes a camel-case string which starts with an upper case character.
func capitalise(input string) string { func capitalise(input string) string {
for len(input) > 0 && input[0] == '_' { return abi.ToCamelCase(input)
input = input[1:]
}
if len(input) == 0 {
return ""
}
return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
} }
// decapitalise makes a camel-case string which starts with a lower case character. // decapitalise makes a camel-case string which starts with a lower case character.
func decapitalise(input string) string { func decapitalise(input string) string {
for len(input) > 0 && input[0] == '_' { // NOTE: This is the current behavior, it doesn't match the comment
input = input[1:] // above and needs to be investigated.
} return abi.ToCamelCase(input)
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
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 // structured checks whether a list of ABI data types has enough information to

View File

@ -186,7 +186,7 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
for _, arg := range args { for _, arg := range args {
abiFieldName := arg.Name abiFieldName := arg.Name
structFieldName := capitalise(abiFieldName) structFieldName := ToCamelCase(abiFieldName)
if structFieldName == "" { if structFieldName == "" {
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct") return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")

View File

@ -310,6 +310,53 @@ var unpackTests = []unpackTest{
Int2 *big.Int Int2 *big.Int
}{big.NewInt(1), big.NewInt(2)}, }{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"}]`, def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",