Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 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, pkg string, lang Lang) (string, error) {
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang) (string, error) {
// Process each individual contract requested binding
contracts := make(map[string]*tmplContract)

Expand Down Expand Up @@ -125,6 +125,9 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
Transacts: transacts,
Events: events,
}
if len(fsigs) > i {
contracts[types[i]].FuncSigs = fsigs[i]
}
}
// Generate the contract template data content and render it
data := &tmplData{
Expand Down Expand Up @@ -180,8 +183,7 @@ func bindBasicTypeGo(kind abi.Type) string {
case abi.BytesTy:
return "[]byte"
case abi.FunctionTy:
// todo(rjl493456442)
return ""
return "[24]byte"
default:
// string, bool types
return kind.String()
Expand Down Expand Up @@ -240,8 +242,7 @@ func bindBasicTypeJava(kind abi.Type) string {
case abi.StringTy:
return "String"
case abi.FunctionTy:
// todo(rjl493456442)
return ""
return "byte[24]"
default:
return kind.String()
}
Expand Down
62 changes: 60 additions & 2 deletions accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var bindTests = []struct {
abi string
imports string
tester string
fsigs []map[string]string
}{
// Test that the binding is available in combined and separate forms too
{
Expand All @@ -55,6 +56,7 @@ var bindTests = []struct {
t.Fatalf("transactor binding (%v) nil or error (%v) not nil", b, nil)
}
`,
nil,
},
// Test that all the official sample contracts bind correctly
{
Expand All @@ -68,6 +70,7 @@ var bindTests = []struct {
t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil)
}
`,
nil,
},
{
`Crowdsale`,
Expand All @@ -80,6 +83,7 @@ var bindTests = []struct {
t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil)
}
`,
nil,
},
{
`DAO`,
Expand All @@ -92,6 +96,7 @@ var bindTests = []struct {
t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil)
}
`,
nil,
},
// Test that named and anonymous inputs are handled correctly
{
Expand Down Expand Up @@ -125,6 +130,7 @@ var bindTests = []struct {

fmt.Println(err)
}`,
nil,
},
// Test that named and anonymous outputs are handled correctly
{
Expand Down Expand Up @@ -161,6 +167,7 @@ var bindTests = []struct {

fmt.Println(str1, str2, res.Str1, res.Str2, err)
}`,
nil,
},
// Tests that named, anonymous and indexed events are handled correctly
{
Expand Down Expand Up @@ -226,6 +233,7 @@ var bindTests = []struct {
if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok {
t.Errorf("binding has disallowed method (FilterAnonymous)")
}`,
nil,
},
// Test that contract interactions (deploy, transact and call) generate working code
{
Expand Down Expand Up @@ -282,6 +290,7 @@ var bindTests = []struct {
t.Fatalf("Transact string mismatch: have '%s', want 'Transact string'", str)
}
`,
nil,
},
// Tests that plain values can be properly returned and deserialized
{
Expand Down Expand Up @@ -322,6 +331,7 @@ var bindTests = []struct {
t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", str, num, "Hi", 1)
}
`,
nil,
},
// Tests that tuples can be properly returned and deserialized
{
Expand Down Expand Up @@ -362,6 +372,7 @@ var bindTests = []struct {
t.Fatalf("Retrieved value mismatch: have %v/%v, want %v/%v", res.A, res.B, "Hi", 1)
}
`,
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 @@ -414,6 +425,7 @@ var bindTests = []struct {
t.Fatalf("Slice return mismatch: have %v, want %v", out, []common.Address{auth.From, common.Address{}})
}
`,
nil,
},
// Tests that anonymous default methods can be correctly invoked
{
Expand Down Expand Up @@ -459,6 +471,7 @@ var bindTests = []struct {
t.Fatalf("Address mismatch: have %v, want %v", caller, auth.From)
}
`,
nil,
},
// Tests that non-existent contracts are reported as such (though only simulator test)
{
Expand Down Expand Up @@ -492,6 +505,7 @@ var bindTests = []struct {
t.Fatalf("Error mismatch: have %v, want %v", err, bind.ErrNoCode)
}
`,
nil,
},
// Tests that gas estimation works for contracts with weird gas mechanics too.
{
Expand Down Expand Up @@ -542,6 +556,7 @@ var bindTests = []struct {
t.Fatalf("Field mismatch: have %v, want %v", field, "automatic")
}
`,
nil,
},
// Test that constant functions can be called from an (optional) specified address
{
Expand Down Expand Up @@ -590,6 +605,7 @@ var bindTests = []struct {
}
}
`,
nil,
},
// Tests that methods and returns with underscores inside work correctly.
{
Expand Down Expand Up @@ -664,6 +680,7 @@ var bindTests = []struct {

fmt.Println(a, b, err)
`,
nil,
},
// Tests that logs can be successfully filtered and decoded.
{
Expand Down Expand Up @@ -880,6 +897,7 @@ var bindTests = []struct {
case <-time.After(250 * time.Millisecond):
}
`,
nil,
},
{
`DeeplyNestedArray`,
Expand Down Expand Up @@ -955,6 +973,46 @@ var bindTests = []struct {
t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err)
}
`,
nil,
},
{
`CallbackParam`,
`
contract FunctionPointerTest {
function test(function(uint256) external callback) external {
callback(1);
}
}
`,
`608060405234801561001057600080fd5b5061015e806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063d7a5aba214610040575b600080fd5b34801561004c57600080fd5b506100be6004803603602081101561006357600080fd5b810190808035806c0100000000000000000000000090049068010000000000000000900463ffffffff1677ffffffffffffffffffffffffffffffffffffffffffffffff169091602001919093929190939291905050506100c0565b005b818160016040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561011657600080fd5b505af115801561012a573d6000803e3d6000fd5b50505050505056fea165627a7a7230582062f87455ff84be90896dbb0c4e4ddb505c600d23089f8e80a512548440d7e2580029`,
`[
{
"constant": false,
"inputs": [
{
"name": "callback",
"type": "function"
}
],
"name": "test",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]`, `
"strings"
`,
`
if strings.Compare("test(function)", CallbackParamFuncSigs["d7a5aba2"]) != 0 {
t.Fatalf("")
}
`,
[]map[string]string{
{
"test(function)": "d7a5aba2",
},
},
},
}

Expand All @@ -980,7 +1038,7 @@ func TestGolangBindings(t *testing.T) {
// Generate the test suite for all the contracts
for i, tt := range bindTests {
// Generate the binding and create a Go source file in the workspace
bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest", LangGo)
bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, tt.fsigs, "bindtest", LangGo)
if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err)
}
Expand Down Expand Up @@ -1399,7 +1457,7 @@ public class Test {
},
}
for i, c := range cases {
binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, "bindtest", LangJava)
binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, nil, "bindtest", LangJava)
if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err)
}
Expand Down
23 changes: 23 additions & 0 deletions accounts/abi/bind/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type tmplContract struct {
Type string // Type name of the main contract binding
InputABI string // JSON ABI used as the input to generate the binding from
InputBin string // Optional EVM bytecode used to denetare deploy code from
FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data
Expand Down Expand Up @@ -92,6 +93,15 @@ var (
// {{.Type}}ABI is the input ABI used to generate the binding from.
const {{.Type}}ABI = "{{.InputABI}}"

{{if $contract.FuncSigs}}
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
var {{.Type}}FuncSigs = map[string]string{
{{range $strsig, $binsig := .FuncSigs}}
"{{$binsig}}": "{{$strsig}}",
{{end}}
}
{{end}}

{{if .InputBin}}
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + `
Expand Down Expand Up @@ -452,12 +462,25 @@ const tmplSourceJava = `
package {{.Package}};

import org.ethereum.geth.*;
import java.util.*;

{{range $contract := .Contracts}}
public class {{.Type}} {
// ABI is the input ABI used to generate the binding from.
public final static String ABI = "{{.InputABI}}";

{{if $contract.FuncSigs}}
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
public final static Map<String, String> {{.Type}}FuncSigs;
static {
Hashtable<String, String> temp = new Hashtable<String, String>();
{{range $strsig, $binsig := .FuncSigs}}
temp.put("{{$binsig}}", "{{$strsig}}");
{{end}}
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
}
{{end}}

{{if .InputBin}}
// BYTECODE is the compiled bytecode used for deploying new contracts.
public final static String BYTECODE = "0x{{.InputBin}}";
Expand Down
4 changes: 3 additions & 1 deletion cmd/abigen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func main() {
abis []string
bins []string
types []string
sigs []map[string]string
)
if *solFlag != "" || *vyFlag != "" || *abiFlag == "-" {
// Generate the list of types to exclude from binding
Expand Down Expand Up @@ -121,6 +122,7 @@ func main() {
}
abis = append(abis, string(abi))
bins = append(bins, contract.Code)
sigs = append(sigs, contract.Hashes)

nameParts := strings.Split(name, ":")
types = append(types, nameParts[len(nameParts)-1])
Expand Down Expand Up @@ -151,7 +153,7 @@ func main() {
types = append(types, kind)
}
// Generate the contract binding
code, err := bind.Bind(types, abis, bins, *pkgFlag, lang)
code, err := bind.Bind(types, abis, bins, sigs, *pkgFlag, lang)
if err != nil {
fmt.Printf("Failed to generate ABI binding: %v\n", err)
os.Exit(-1)
Expand Down
7 changes: 4 additions & 3 deletions common/compiler/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`)

// Contract contains information about a compiled contract, alongside its code and runtime code.
type Contract struct {
Code string `json:"code"`
RuntimeCode string `json:"runtime-code"`
Info ContractInfo `json:"info"`
Code string `json:"code"`
RuntimeCode string `json:"runtime-code"`
Info ContractInfo `json:"info"`
Hashes map[string]string `json:"hashes"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any need to keep this backwards compatible, or do we assume that ppl always regenerate stuff and there's no need to parse stuff which doesn't have hashes ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is backwards-compatible in the sense that this is a convenience conversion table so that client code can quickly lookup a function name given its hash. Older client code that won't be regenerated will simply have to do the translation themselves.

}

// ContractInfo contains information about a compiled contract, including access
Expand Down
4 changes: 3 additions & 1 deletion common/compiler/solidity.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type solcOutput struct {
BinRuntime string `json:"bin-runtime"`
SrcMapRuntime string `json:"srcmap-runtime"`
Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string
Hashes map[string]string
}
Version string
}
Expand All @@ -49,7 +50,7 @@ func (s *Solidity) makeArgs() []string {
"--optimize", // code optimizer switched on
}
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {
p[1] += ",metadata"
p[1] += ",metadata,hashes"
}
return p
}
Expand Down Expand Up @@ -161,6 +162,7 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin
contracts[name] = &Contract{
Code: "0x" + info.Bin,
RuntimeCode: "0x" + info.BinRuntime,
Hashes: info.Hashes,
Info: ContractInfo{
Source: source,
Language: "Solidity",
Expand Down