github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/abi/bind/bind.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:26</date>
    10  //</624342582792556544>
    11  
    12  
    13  //包绑定生成以太坊契约go绑定。
    14  //
    15  //有关详细的使用文档和教程,请访问以太坊wiki页面:
    16  //https://github.com/ethereum/go-ethereum/wiki/native-dapps:-转到绑定到ethereum合同
    17  package bind
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"regexp"
    23  	"strings"
    24  	"text/template"
    25  	"unicode"
    26  
    27  	"github.com/ethereum/go-ethereum/accounts/abi"
    28  	"golang.org/x/tools/imports"
    29  )
    30  
    31  //lang是为其生成绑定的目标编程语言选择器。
    32  type Lang int
    33  
    34  const (
    35  	LangGo Lang = iota
    36  	LangJava
    37  	LangObjC
    38  )
    39  
    40  //bind围绕一个契约abi生成一个go包装器。这个包装不代表
    41  //在客户端代码中使用,而不是作为中间结构使用,
    42  //强制执行编译时类型安全和命名约定,而不是必须
    43  //手动维护在运行时中断的硬编码字符串。
    44  func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
    45  //处理每个要求约束的单独合同
    46  	contracts := make(map[string]*tmplContract)
    47  
    48  	for i := 0; i < len(types); i++ {
    49  //分析实际的ABI以生成绑定
    50  		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
    51  		if err != nil {
    52  			return "", err
    53  		}
    54  //从JSONABI中删除任何空白
    55  		strippedABI := strings.Map(func(r rune) rune {
    56  			if unicode.IsSpace(r) {
    57  				return -1
    58  			}
    59  			return r
    60  		}, abis[i])
    61  
    62  //提取调用和事务处理方法;事件;并按字母顺序排序
    63  		var (
    64  			calls     = make(map[string]*tmplMethod)
    65  			transacts = make(map[string]*tmplMethod)
    66  			events    = make(map[string]*tmplEvent)
    67  		)
    68  		for _, original := range evmABI.Methods {
    69  //规范资本案例和非匿名输入/输出的方法
    70  			normalized := original
    71  			normalized.Name = methodNormalizer[lang](original.Name)
    72  
    73  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
    74  			copy(normalized.Inputs, original.Inputs)
    75  			for j, input := range normalized.Inputs {
    76  				if input.Name == "" {
    77  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
    78  				}
    79  			}
    80  			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
    81  			copy(normalized.Outputs, original.Outputs)
    82  			for j, output := range normalized.Outputs {
    83  				if output.Name != "" {
    84  					normalized.Outputs[j].Name = capitalise(output.Name)
    85  				}
    86  			}
    87  //将方法附加到调用或事务处理列表中
    88  			if original.Const {
    89  				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
    90  			} else {
    91  				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
    92  			}
    93  		}
    94  		for _, original := range evmABI.Events {
    95  //跳过匿名事件,因为它们不支持显式筛选
    96  			if original.Anonymous {
    97  				continue
    98  			}
    99  //规范资本案例和非匿名输出的事件
   100  			normalized := original
   101  			normalized.Name = methodNormalizer[lang](original.Name)
   102  
   103  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   104  			copy(normalized.Inputs, original.Inputs)
   105  			for j, input := range normalized.Inputs {
   106  //索引字段是输入,非索引字段是输出
   107  				if input.Indexed {
   108  					if input.Name == "" {
   109  						normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   110  					}
   111  				}
   112  			}
   113  //将事件追加到累加器列表
   114  			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
   115  		}
   116  		contracts[types[i]] = &tmplContract{
   117  			Type:        capitalise(types[i]),
   118  			InputABI:    strings.Replace(strippedABI, "\"", "\\\"", -1),
   119  			InputBin:    strings.TrimSpace(bytecodes[i]),
   120  			Constructor: evmABI.Constructor,
   121  			Calls:       calls,
   122  			Transacts:   transacts,
   123  			Events:      events,
   124  		}
   125  	}
   126  //生成合同模板数据内容并呈现
   127  	data := &tmplData{
   128  		Package:   pkg,
   129  		Contracts: contracts,
   130  	}
   131  	buffer := new(bytes.Buffer)
   132  
   133  	funcs := map[string]interface{}{
   134  		"bindtype":      bindType[lang],
   135  		"bindtopictype": bindTopicType[lang],
   136  		"namedtype":     namedType[lang],
   137  		"capitalise":    capitalise,
   138  		"decapitalise":  decapitalise,
   139  	}
   140  	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
   141  	if err := tmpl.Execute(buffer, data); err != nil {
   142  		return "", err
   143  	}
   144  //for go绑定通过goimports传递代码以清除它并进行双重检查
   145  	if lang == LangGo {
   146  		code, err := imports.Process(".", buffer.Bytes(), nil)
   147  		if err != nil {
   148  			return "", fmt.Errorf("%v\n%s", err, buffer)
   149  		}
   150  		return string(code), nil
   151  	}
   152  //对于所有其他人来说,现在就按原样返回
   153  	return buffer.String(), nil
   154  }
   155  
   156  //bindtype是一组类型绑定器,可以将solidity类型转换为支持的类型
   157  //编程语言类型。
   158  var bindType = map[Lang]func(kind abi.Type) string{
   159  	LangGo:   bindTypeGo,
   160  	LangJava: bindTypeJava,
   161  }
   162  
   163  //绑定生成器的助手函数。
   164  //在内部类型匹配后读取不匹配的字符,
   165  //(因为内部类型是total类型声明的前缀),
   166  //查找包装内部类型的有效数组(可能是动态数组),
   167  //并返回这些数组的大小。
   168  //
   169  //返回的数组大小与solidity签名的顺序相同;首先是内部数组大小。
   170  //数组大小也可以为“”,表示动态数组。
   171  func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
   172  	remainder := stringKind[innerLen:]
   173  //找到所有尺寸
   174  	matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
   175  	parts := make([]string, 0, len(matches))
   176  	for _, match := range matches {
   177  //从正则表达式匹配中获取组1
   178  		parts = append(parts, match[1])
   179  	}
   180  	return innerMapping, parts
   181  }
   182  
   183  //将数组大小转换为内部类型(嵌套)数组的Go-Lang声明。
   184  //如果arraysizes为空,只返回内部类型。
   185  func arrayBindingGo(inner string, arraySizes []string) string {
   186  	out := ""
   187  //预先处理所有阵列大小,从外部(结束阵列大小)到内部(开始阵列大小)
   188  	for i := len(arraySizes) - 1; i >= 0; i-- {
   189  		out += "[" + arraySizes[i] + "]"
   190  	}
   191  	out += inner
   192  	return out
   193  }
   194  
   195  //bindtypego将solidity类型转换为go类型。因为没有清晰的地图
   196  //从所有solidity类型到go类型(例如uint17),那些不能精确
   197  //mapped将使用升序类型(例如*big.int)。
   198  func bindTypeGo(kind abi.Type) string {
   199  	stringKind := kind.String()
   200  	innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
   201  	return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
   202  }
   203  
   204  //bindtypego的内部函数,它查找StringKind的内部类型。
   205  //(或者,如果类型本身不是数组或切片,则仅限于该类型本身)
   206  //将返回匹配部分的长度和转换后的类型。
   207  func bindUnnestedTypeGo(stringKind string) (int, string) {
   208  
   209  	switch {
   210  	case strings.HasPrefix(stringKind, "address"):
   211  		return len("address"), "common.Address"
   212  
   213  	case strings.HasPrefix(stringKind, "bytes"):
   214  		parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
   215  		return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
   216  
   217  	case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
   218  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
   219  		switch parts[2] {
   220  		case "8", "16", "32", "64":
   221  			return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
   222  		}
   223  		return len(parts[0]), "*big.Int"
   224  
   225  	case strings.HasPrefix(stringKind, "bool"):
   226  		return len("bool"), "bool"
   227  
   228  	case strings.HasPrefix(stringKind, "string"):
   229  		return len("string"), "string"
   230  
   231  	default:
   232  		return len(stringKind), stringKind
   233  	}
   234  }
   235  
   236  //将数组大小转换为内部类型(嵌套)数组的Java声明。
   237  //如果arraysizes为空,只返回内部类型。
   238  func arrayBindingJava(inner string, arraySizes []string) string {
   239  //Java数组类型声明不包括长度。
   240  	return inner + strings.Repeat("[]", len(arraySizes))
   241  }
   242  
   243  //bdType Java将一个稳固类型转换为Java类型。因为没有清晰的地图
   244  //从所有Solidity类型到Java类型(例如UIT17),那些不能精确的类型。
   245  //mapped将使用升序类型(例如bigdecimal)。
   246  func bindTypeJava(kind abi.Type) string {
   247  	stringKind := kind.String()
   248  	innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
   249  	return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
   250  }
   251  
   252  //bindtypejava的内部函数,它查找StringKind的内部类型。
   253  //(或者,如果类型本身不是数组或切片,则仅限于该类型本身)
   254  //将返回匹配部分的长度和转换后的类型。
   255  func bindUnnestedTypeJava(stringKind string) (int, string) {
   256  
   257  	switch {
   258  	case strings.HasPrefix(stringKind, "address"):
   259  		parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
   260  		if len(parts) != 2 {
   261  			return len(stringKind), stringKind
   262  		}
   263  		if parts[1] == "" {
   264  			return len("address"), "Address"
   265  		}
   266  		return len(parts[0]), "Addresses"
   267  
   268  	case strings.HasPrefix(stringKind, "bytes"):
   269  		parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
   270  		if len(parts) != 2 {
   271  			return len(stringKind), stringKind
   272  		}
   273  		return len(parts[0]), "byte[]"
   274  
   275  	case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
   276  //注意,uint和int(不带数字)也匹配,
   277  //它们的大小为256,将转换为bigint(默认值)。
   278  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
   279  		if len(parts) != 3 {
   280  			return len(stringKind), stringKind
   281  		}
   282  
   283  		namedSize := map[string]string{
   284  			"8":  "byte",
   285  			"16": "short",
   286  			"32": "int",
   287  			"64": "long",
   288  		}[parts[2]]
   289  
   290  //默认为bigint
   291  		if namedSize == "" {
   292  			namedSize = "BigInt"
   293  		}
   294  		return len(parts[0]), namedSize
   295  
   296  	case strings.HasPrefix(stringKind, "bool"):
   297  		return len("bool"), "boolean"
   298  
   299  	case strings.HasPrefix(stringKind, "string"):
   300  		return len("string"), "String"
   301  
   302  	default:
   303  		return len(stringKind), stringKind
   304  	}
   305  }
   306  
   307  //bindtopictype是一组类型绑定器,将solidity类型转换为
   308  //支持的编程语言主题类型。
   309  var bindTopicType = map[Lang]func(kind abi.Type) string{
   310  	LangGo:   bindTopicTypeGo,
   311  	LangJava: bindTopicTypeJava,
   312  }
   313  
   314  //bindtypego将solidity主题类型转换为go类型。几乎是一样的
   315  //功能与简单类型相同,但动态类型转换为哈希。
   316  func bindTopicTypeGo(kind abi.Type) string {
   317  	bound := bindTypeGo(kind)
   318  	if bound == "string" || bound == "[]byte" {
   319  		bound = "common.Hash"
   320  	}
   321  	return bound
   322  }
   323  
   324  //bdIdTyGO将一个坚固性主题类型转换为Java主题类型。几乎是一样的
   325  //功能与简单类型相同,但动态类型转换为哈希。
   326  func bindTopicTypeJava(kind abi.Type) string {
   327  	bound := bindTypeJava(kind)
   328  	if bound == "String" || bound == "Bytes" {
   329  		bound = "Hash"
   330  	}
   331  	return bound
   332  }
   333  
   334  //NamedType是一组将特定语言类型转换为
   335  //方法名中使用的命名版本。
   336  var namedType = map[Lang]func(string, abi.Type) string{
   337  	LangGo:   func(string, abi.Type) string { panic("this shouldn't be needed") },
   338  	LangJava: namedTypeJava,
   339  }
   340  
   341  //NamedTypeJava将一些基元数据类型转换为可以
   342  //用作方法名称的一部分。
   343  func namedTypeJava(javaKind string, solKind abi.Type) string {
   344  	switch javaKind {
   345  	case "byte[]":
   346  		return "Binary"
   347  	case "byte[][]":
   348  		return "Binaries"
   349  	case "string":
   350  		return "String"
   351  	case "string[]":
   352  		return "Strings"
   353  	case "boolean":
   354  		return "Bool"
   355  	case "boolean[]":
   356  		return "Bools"
   357  	case "BigInt[]":
   358  		return "BigInts"
   359  	default:
   360  		parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
   361  		if len(parts) != 4 {
   362  			return javaKind
   363  		}
   364  		switch parts[2] {
   365  		case "8", "16", "32", "64":
   366  			if parts[3] == "" {
   367  				return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
   368  			}
   369  			return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
   370  
   371  		default:
   372  			return javaKind
   373  		}
   374  	}
   375  }
   376  
   377  //methodNormalizer是一个名称转换器,它将solidity方法名称修改为
   378  //符合目标语言命名概念。
   379  var methodNormalizer = map[Lang]func(string) string{
   380  	LangGo:   capitalise,
   381  	LangJava: decapitalise,
   382  }
   383  
   384  //capitale生成一个以大写字符开头的驼色大小写字符串。
   385  func capitalise(input string) string {
   386  	for len(input) > 0 && input[0] == '_' {
   387  		input = input[1:]
   388  	}
   389  	if len(input) == 0 {
   390  		return ""
   391  	}
   392  	return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
   393  }
   394  
   395  //无头化生成一个以小写字符开头的驼色大小写字符串。
   396  func decapitalise(input string) string {
   397  	for len(input) > 0 && input[0] == '_' {
   398  		input = input[1:]
   399  	}
   400  	if len(input) == 0 {
   401  		return ""
   402  	}
   403  	return toCamelCase(strings.ToLower(input[:1]) + input[1:])
   404  }
   405  
   406  //to camel case将欠分数字符串转换为驼色大小写字符串
   407  func toCamelCase(input string) string {
   408  	toupper := false
   409  
   410  	result := ""
   411  	for k, v := range input {
   412  		switch {
   413  		case k == 0:
   414  			result = strings.ToUpper(string(input[0]))
   415  
   416  		case toupper:
   417  			result += strings.ToUpper(string(v))
   418  			toupper = false
   419  
   420  		case v == '_':
   421  			toupper = true
   422  
   423  		default:
   424  			result += string(v)
   425  		}
   426  	}
   427  	return result
   428  }
   429  
   430  //结构化检查ABI数据类型列表是否有足够的信息
   431  //通过适当的go结构或如果需要平面返回,则进行操作。
   432  func structured(args abi.Arguments) bool {
   433  	if len(args) < 2 {
   434  		return false
   435  	}
   436  	exists := make(map[string]bool)
   437  	for _, out := range args {
   438  //如果名称是匿名的,则无法组织成结构
   439  		if out.Name == "" {
   440  			return false
   441  		}
   442  //如果规范化或冲突时字段名为空(var、var、_var、_var),
   443  //我们不能组织成一个结构
   444  		field := capitalise(out.Name)
   445  		if field == "" || exists[field] {
   446  			return false
   447  		}
   448  		exists[field] = true
   449  	}
   450  	return true
   451  }
   452