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