1// Copyright 2016 The go-ethereum Authors
2// This file is part of the go-ethereum library.
3//
4// The go-ethereum library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Lesser General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// The go-ethereum library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Lesser General Public License for more details.
13//
14// You should have received a copy of the GNU Lesser General Public License
15// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16
17// Package bind generates Ethereum contract Go bindings.
18//
19// Detailed usage document and tutorial available on the go-ethereum Wiki page:
20// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
21package bind
22
23import (
24	"bytes"
25	"errors"
26	"fmt"
27	"go/format"
28	"regexp"
29	"strings"
30	"text/template"
31	"unicode"
32
33	"github.com/ethereum/go-ethereum/accounts/abi"
34	"github.com/ethereum/go-ethereum/log"
35)
36
37// Lang is a target programming language selector to generate bindings for.
38type Lang int
39
40const (
41	LangGo Lang = iota
42	LangJava
43	LangObjC
44)
45
46// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
47// to be used as is in client code, but rather as an intermediate struct which
48// enforces compile time type safety and naming convention opposed to having to
49// manually maintain hard coded strings that break on runtime.
50func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
51	var (
52		// contracts is the map of each individual contract requested binding
53		contracts = make(map[string]*tmplContract)
54
55		// structs is the map of all redeclared structs shared by passed contracts.
56		structs = make(map[string]*tmplStruct)
57
58		// isLib is the map used to flag each encountered library as such
59		isLib = make(map[string]struct{})
60	)
61	for i := 0; i < len(types); i++ {
62		// Parse the actual ABI to generate the binding for
63		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
64		if err != nil {
65			return "", err
66		}
67		// Strip any whitespace from the JSON ABI
68		strippedABI := strings.Map(func(r rune) rune {
69			if unicode.IsSpace(r) {
70				return -1
71			}
72			return r
73		}, abis[i])
74
75		// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
76		var (
77			calls     = make(map[string]*tmplMethod)
78			transacts = make(map[string]*tmplMethod)
79			events    = make(map[string]*tmplEvent)
80			fallback  *tmplMethod
81			receive   *tmplMethod
82
83			// identifiers are used to detect duplicated identifiers of functions
84			// and events. For all calls, transacts and events, abigen will generate
85			// corresponding bindings. However we have to ensure there is no
86			// identifier collisions in the bindings of these categories.
87			callIdentifiers     = make(map[string]bool)
88			transactIdentifiers = make(map[string]bool)
89			eventIdentifiers    = make(map[string]bool)
90		)
91
92		for _, input := range evmABI.Constructor.Inputs {
93			if hasStruct(input.Type) {
94				bindStructType[lang](input.Type, structs)
95			}
96		}
97
98		for _, original := range evmABI.Methods {
99			// Normalize the method for capital cases and non-anonymous inputs/outputs
100			normalized := original
101			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
102			// Ensure there is no duplicated identifier
103			var identifiers = callIdentifiers
104			if !original.IsConstant() {
105				identifiers = transactIdentifiers
106			}
107			if identifiers[normalizedName] {
108				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
109			}
110			identifiers[normalizedName] = true
111			normalized.Name = normalizedName
112			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
113			copy(normalized.Inputs, original.Inputs)
114			for j, input := range normalized.Inputs {
115				if input.Name == "" {
116					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
117				}
118				if hasStruct(input.Type) {
119					bindStructType[lang](input.Type, structs)
120				}
121			}
122			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
123			copy(normalized.Outputs, original.Outputs)
124			for j, output := range normalized.Outputs {
125				if output.Name != "" {
126					normalized.Outputs[j].Name = capitalise(output.Name)
127				}
128				if hasStruct(output.Type) {
129					bindStructType[lang](output.Type, structs)
130				}
131			}
132			// Append the methods to the call or transact lists
133			if original.IsConstant() {
134				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
135			} else {
136				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
137			}
138		}
139		for _, original := range evmABI.Events {
140			// Skip anonymous events as they don't support explicit filtering
141			if original.Anonymous {
142				continue
143			}
144			// Normalize the event for capital cases and non-anonymous outputs
145			normalized := original
146
147			// Ensure there is no duplicated identifier
148			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
149			if eventIdentifiers[normalizedName] {
150				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
151			}
152			eventIdentifiers[normalizedName] = true
153			normalized.Name = normalizedName
154
155			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
156			copy(normalized.Inputs, original.Inputs)
157			for j, input := range normalized.Inputs {
158				if input.Name == "" {
159					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
160				}
161				if hasStruct(input.Type) {
162					bindStructType[lang](input.Type, structs)
163				}
164			}
165			// Append the event to the accumulator list
166			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
167		}
168		// Add two special fallback functions if they exist
169		if evmABI.HasFallback() {
170			fallback = &tmplMethod{Original: evmABI.Fallback}
171		}
172		if evmABI.HasReceive() {
173			receive = &tmplMethod{Original: evmABI.Receive}
174		}
175		// There is no easy way to pass arbitrary java objects to the Go side.
176		if len(structs) > 0 && lang == LangJava {
177			return "", errors.New("java binding for tuple arguments is not supported yet")
178		}
179
180		contracts[types[i]] = &tmplContract{
181			Type:        capitalise(types[i]),
182			InputABI:    strings.Replace(strippedABI, "\"", "\\\"", -1),
183			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
184			Constructor: evmABI.Constructor,
185			Calls:       calls,
186			Transacts:   transacts,
187			Fallback:    fallback,
188			Receive:     receive,
189			Events:      events,
190			Libraries:   make(map[string]string),
191		}
192		// Function 4-byte signatures are stored in the same sequence
193		// as types, if available.
194		if len(fsigs) > i {
195			contracts[types[i]].FuncSigs = fsigs[i]
196		}
197		// Parse library references.
198		for pattern, name := range libs {
199			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
200			if err != nil {
201				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
202			}
203			if matched {
204				contracts[types[i]].Libraries[pattern] = name
205				// keep track that this type is a library
206				if _, ok := isLib[name]; !ok {
207					isLib[name] = struct{}{}
208				}
209			}
210		}
211	}
212	// Check if that type has already been identified as a library
213	for i := 0; i < len(types); i++ {
214		_, ok := isLib[types[i]]
215		contracts[types[i]].Library = ok
216	}
217	// Generate the contract template data content and render it
218	data := &tmplData{
219		Package:   pkg,
220		Contracts: contracts,
221		Libraries: libs,
222		Structs:   structs,
223	}
224	buffer := new(bytes.Buffer)
225
226	funcs := map[string]interface{}{
227		"bindtype":      bindType[lang],
228		"bindtopictype": bindTopicType[lang],
229		"namedtype":     namedType[lang],
230		"capitalise":    capitalise,
231		"decapitalise":  decapitalise,
232	}
233	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
234	if err := tmpl.Execute(buffer, data); err != nil {
235		return "", err
236	}
237	// For Go bindings pass the code through gofmt to clean it up
238	if lang == LangGo {
239		code, err := format.Source(buffer.Bytes())
240		if err != nil {
241			return "", fmt.Errorf("%v\n%s", err, buffer)
242		}
243		return string(code), nil
244	}
245	// For all others just return as is for now
246	return buffer.String(), nil
247}
248
249// bindType is a set of type binders that convert Solidity types to some supported
250// programming language types.
251var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
252	LangGo:   bindTypeGo,
253	LangJava: bindTypeJava,
254}
255
256// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
257func bindBasicTypeGo(kind abi.Type) string {
258	switch kind.T {
259	case abi.AddressTy:
260		return "common.Address"
261	case abi.IntTy, abi.UintTy:
262		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
263		switch parts[2] {
264		case "8", "16", "32", "64":
265			return fmt.Sprintf("%sint%s", parts[1], parts[2])
266		}
267		return "*big.Int"
268	case abi.FixedBytesTy:
269		return fmt.Sprintf("[%d]byte", kind.Size)
270	case abi.BytesTy:
271		return "[]byte"
272	case abi.FunctionTy:
273		return "[24]byte"
274	default:
275		// string, bool types
276		return kind.String()
277	}
278}
279
280// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
281// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
282// mapped will use an upscaled type (e.g. BigDecimal).
283func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
284	switch kind.T {
285	case abi.TupleTy:
286		return structs[kind.TupleRawName+kind.String()].Name
287	case abi.ArrayTy:
288		return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
289	case abi.SliceTy:
290		return "[]" + bindTypeGo(*kind.Elem, structs)
291	default:
292		return bindBasicTypeGo(kind)
293	}
294}
295
296// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones.
297func bindBasicTypeJava(kind abi.Type) string {
298	switch kind.T {
299	case abi.AddressTy:
300		return "Address"
301	case abi.IntTy, abi.UintTy:
302		// Note that uint and int (without digits) are also matched,
303		// these are size 256, and will translate to BigInt (the default).
304		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
305		if len(parts) != 3 {
306			return kind.String()
307		}
308		// All unsigned integers should be translated to BigInt since gomobile doesn't
309		// support them.
310		if parts[1] == "u" {
311			return "BigInt"
312		}
313
314		namedSize := map[string]string{
315			"8":  "byte",
316			"16": "short",
317			"32": "int",
318			"64": "long",
319		}[parts[2]]
320
321		// default to BigInt
322		if namedSize == "" {
323			namedSize = "BigInt"
324		}
325		return namedSize
326	case abi.FixedBytesTy, abi.BytesTy:
327		return "byte[]"
328	case abi.BoolTy:
329		return "boolean"
330	case abi.StringTy:
331		return "String"
332	case abi.FunctionTy:
333		return "byte[24]"
334	default:
335		return kind.String()
336	}
337}
338
339// pluralizeJavaType explicitly converts multidimensional types to predefined
340// types in go side.
341func pluralizeJavaType(typ string) string {
342	switch typ {
343	case "boolean":
344		return "Bools"
345	case "String":
346		return "Strings"
347	case "Address":
348		return "Addresses"
349	case "byte[]":
350		return "Binaries"
351	case "BigInt":
352		return "BigInts"
353	}
354	return typ + "[]"
355}
356
357// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
358// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
359// mapped will use an upscaled type (e.g. BigDecimal).
360func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
361	switch kind.T {
362	case abi.TupleTy:
363		return structs[kind.TupleRawName+kind.String()].Name
364	case abi.ArrayTy, abi.SliceTy:
365		return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
366	default:
367		return bindBasicTypeJava(kind)
368	}
369}
370
371// bindTopicType is a set of type binders that convert Solidity types to some
372// supported programming language topic types.
373var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
374	LangGo:   bindTopicTypeGo,
375	LangJava: bindTopicTypeJava,
376}
377
378// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
379// functionality as for simple types, but dynamic types get converted to hashes.
380func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
381	bound := bindTypeGo(kind, structs)
382
383	// todo(rjl493456442) according solidity documentation, indexed event
384	// parameters that are not value types i.e. arrays and structs are not
385	// stored directly but instead a keccak256-hash of an encoding is stored.
386	//
387	// We only convert stringS and bytes to hash, still need to deal with
388	// array(both fixed-size and dynamic-size) and struct.
389	if bound == "string" || bound == "[]byte" {
390		bound = "common.Hash"
391	}
392	return bound
393}
394
395// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
396// functionality as for simple types, but dynamic types get converted to hashes.
397func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
398	bound := bindTypeJava(kind, structs)
399
400	// todo(rjl493456442) according solidity documentation, indexed event
401	// parameters that are not value types i.e. arrays and structs are not
402	// stored directly but instead a keccak256-hash of an encoding is stored.
403	//
404	// We only convert strings and bytes to hash, still need to deal with
405	// array(both fixed-size and dynamic-size) and struct.
406	if bound == "String" || bound == "byte[]" {
407		bound = "Hash"
408	}
409	return bound
410}
411
412// bindStructType is a set of type binders that convert Solidity tuple types to some supported
413// programming language struct definition.
414var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
415	LangGo:   bindStructTypeGo,
416	LangJava: bindStructTypeJava,
417}
418
419// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
420// in the given map.
421// Notably, this function will resolve and record nested struct recursively.
422func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
423	switch kind.T {
424	case abi.TupleTy:
425		// We compose a raw struct name and a canonical parameter expression
426		// together here. The reason is before solidity v0.5.11, kind.TupleRawName
427		// is empty, so we use canonical parameter expression to distinguish
428		// different struct definition. From the consideration of backward
429		// compatibility, we concat these two together so that if kind.TupleRawName
430		// is not empty, it can have unique id.
431		id := kind.TupleRawName + kind.String()
432		if s, exist := structs[id]; exist {
433			return s.Name
434		}
435		var fields []*tmplField
436		for i, elem := range kind.TupleElems {
437			field := bindStructTypeGo(*elem, structs)
438			fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
439		}
440		name := kind.TupleRawName
441		if name == "" {
442			name = fmt.Sprintf("Struct%d", len(structs))
443		}
444		structs[id] = &tmplStruct{
445			Name:   name,
446			Fields: fields,
447		}
448		return name
449	case abi.ArrayTy:
450		return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
451	case abi.SliceTy:
452		return "[]" + bindStructTypeGo(*kind.Elem, structs)
453	default:
454		return bindBasicTypeGo(kind)
455	}
456}
457
458// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
459// in the given map.
460// Notably, this function will resolve and record nested struct recursively.
461func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
462	switch kind.T {
463	case abi.TupleTy:
464		// We compose a raw struct name and a canonical parameter expression
465		// together here. The reason is before solidity v0.5.11, kind.TupleRawName
466		// is empty, so we use canonical parameter expression to distinguish
467		// different struct definition. From the consideration of backward
468		// compatibility, we concat these two together so that if kind.TupleRawName
469		// is not empty, it can have unique id.
470		id := kind.TupleRawName + kind.String()
471		if s, exist := structs[id]; exist {
472			return s.Name
473		}
474		var fields []*tmplField
475		for i, elem := range kind.TupleElems {
476			field := bindStructTypeJava(*elem, structs)
477			fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
478		}
479		name := kind.TupleRawName
480		if name == "" {
481			name = fmt.Sprintf("Class%d", len(structs))
482		}
483		structs[id] = &tmplStruct{
484			Name:   name,
485			Fields: fields,
486		}
487		return name
488	case abi.ArrayTy, abi.SliceTy:
489		return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
490	default:
491		return bindBasicTypeJava(kind)
492	}
493}
494
495// namedType is a set of functions that transform language specific types to
496// named versions that may be used inside method names.
497var namedType = map[Lang]func(string, abi.Type) string{
498	LangGo:   func(string, abi.Type) string { panic("this shouldn't be needed") },
499	LangJava: namedTypeJava,
500}
501
502// namedTypeJava converts some primitive data types to named variants that can
503// be used as parts of method names.
504func namedTypeJava(javaKind string, solKind abi.Type) string {
505	switch javaKind {
506	case "byte[]":
507		return "Binary"
508	case "boolean":
509		return "Bool"
510	default:
511		parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
512		if len(parts) != 4 {
513			return javaKind
514		}
515		switch parts[2] {
516		case "8", "16", "32", "64":
517			if parts[3] == "" {
518				return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
519			}
520			return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
521
522		default:
523			return javaKind
524		}
525	}
526}
527
528// alias returns an alias of the given string based on the aliasing rules
529// or returns itself if no rule is matched.
530func alias(aliases map[string]string, n string) string {
531	if alias, exist := aliases[n]; exist {
532		return alias
533	}
534	return n
535}
536
537// methodNormalizer is a name transformer that modifies Solidity method names to
538// conform to target language naming conventions.
539var methodNormalizer = map[Lang]func(string) string{
540	LangGo:   abi.ToCamelCase,
541	LangJava: decapitalise,
542}
543
544// capitalise makes a camel-case string which starts with an upper case character.
545var capitalise = abi.ToCamelCase
546
547// decapitalise makes a camel-case string which starts with a lower case character.
548func decapitalise(input string) string {
549	if len(input) == 0 {
550		return input
551	}
552
553	goForm := abi.ToCamelCase(input)
554	return strings.ToLower(goForm[:1]) + goForm[1:]
555}
556
557// structured checks whether a list of ABI data types has enough information to
558// operate through a proper Go struct or if flat returns are needed.
559func structured(args abi.Arguments) bool {
560	if len(args) < 2 {
561		return false
562	}
563	exists := make(map[string]bool)
564	for _, out := range args {
565		// If the name is anonymous, we can't organize into a struct
566		if out.Name == "" {
567			return false
568		}
569		// If the field name is empty when normalized or collides (var, Var, _var, _Var),
570		// we can't organize into a struct
571		field := capitalise(out.Name)
572		if field == "" || exists[field] {
573			return false
574		}
575		exists[field] = true
576	}
577	return true
578}
579
580// hasStruct returns an indicator whether the given type is struct, struct slice
581// or struct array.
582func hasStruct(t abi.Type) bool {
583	switch t.T {
584	case abi.SliceTy:
585		return hasStruct(*t.Elem)
586	case abi.ArrayTy:
587		return hasStruct(*t.Elem)
588	case abi.TupleTy:
589		return true
590	default:
591		return false
592	}
593}
594