From fc4fffd5ac341b6e7c9644045b66420f777940c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 5 Apr 2016 18:20:50 +0300 Subject: [PATCH] accounts/abi/bind: support generating bindings for various arrays --- accounts/abi/bind/bind.go | 43 +++++++++++++++++++-------------- accounts/abi/bind/bind_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index a9f21b21a..24fe9f770 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -23,6 +23,7 @@ package bind import ( "bytes" "fmt" + "regexp" "strings" "text/template" "unicode" @@ -122,31 +123,37 @@ func bindType(kind abi.Type) string { stringKind := kind.String() switch { - case stringKind == "address": - return "common.Address" - - case stringKind == "address[]": - return "[]common.Address" + case strings.HasPrefix(stringKind, "address"): + parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 2 { + return stringKind + } + return fmt.Sprintf("%scommon.Address", parts[1]) case strings.HasPrefix(stringKind, "bytes"): - if stringKind == "bytes" { - return "[]byte" - } - return fmt.Sprintf("[%s]byte", stringKind[5:]) - - case strings.HasPrefix(stringKind, "int"): - switch stringKind[:3] { - case "8", "16", "32", "64": + parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 3 { return stringKind } - return "*big.Int" + return fmt.Sprintf("%s[%s]byte", parts[2], parts[1]) - case strings.HasPrefix(stringKind, "uint"): - switch stringKind[:4] { - case "8", "16", "32", "64": + case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"): + parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 4 { return stringKind } - return "*big.Int" + switch parts[2] { + case "8", "16", "32", "64": + return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2]) + } + return fmt.Sprintf("%s*big.Int", parts[3]) + + case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"): + parts := regexp.MustCompile("([a-z]+)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind) + if len(parts) != 3 { + return stringKind + } + return fmt.Sprintf("%s%s", parts[2], parts[1]) default: return stringKind diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index 12c849669..3f02af017 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -228,6 +228,50 @@ var bindTests = []struct { } `, }, + // Tests that arrays/slices can be properly returned and deserialized. + // Only addresses are tested, remainder just compiled to keep the test small. + { + `Slicer`, + ` + contract Slicer { + function echoAddresses(address[] input) constant returns (address[] output) { + return input; + } + function echoInts(int[] input) constant returns (int[] output) { + return input; + } + function echoFancyInts(uint24[23] input) constant returns (uint24[23] output) { + return input; + } + function echoBools(bool[] input) constant returns (bool[] output) { + return input; + } + } + `, + `606060405261015c806100126000396000f3606060405260e060020a6000350463be1127a3811461003c578063d88becc014610092578063e15a3db71461003c578063f637e5891461003c575b005b604080516020600480358082013583810285810185019096528085526100ee959294602494909392850192829185019084908082843750949650505050505050604080516020810190915260009052805b919050565b604080516102e0818101909252610138916004916102e491839060179083908390808284375090955050505050506102e0604051908101604052806017905b60008152602001906001900390816100d15790505081905061008d565b60405180806020018281038252838181518152602001915080519060200190602002808383829060006004602084601f0104600f02600301f1509050019250505060405180910390f35b60405180826102e0808381846000600461015cf15090500191505060405180910390f3`, + `[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint24[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint24[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`, + ` + // Generate a new random account and a funded simulator + key := crypto.NewKey(rand.Reader) + sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: key.Address, Balance: big.NewInt(10000000000)}) + + // Convert the tester key to an authorized transactor for ease of use + auth := bind.NewKeyedTransactor(key) + + // Deploy a slice tester contract and execute a n array call on it + _, _, slicer, err := DeploySlicer(auth, sim) + if err != nil { + t.Fatalf("Failed to deploy slicer contract: %v", err) + } + sim.Commit() + + if out, err := slicer.EchoAddresses(nil, []common.Address{key.Address, common.Address{}}); err != nil { + t.Fatalf("Failed to call slice echoer: %v", err) + } else if !reflect.DeepEqual(out, []common.Address{key.Address, common.Address{}}) { + t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{key.Address, common.Address{}}) + } + `, + }, // Tests that anonymous default methods can be correctly invoked { `Defaulter`,