github.com/CommerciumBlockchain/go-commercium@v0.0.0-20220709212705-b46438a77516/accounts/abi/bind/bind.go (about)

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