github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/signer/core/abihelper.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 19:16:42</date> 10 //</624450110155132928> 11 12 13 package core 14 15 import ( 16 "encoding/json" 17 "fmt" 18 "io/ioutil" 19 "strings" 20 21 "github.com/ethereum/go-ethereum/accounts/abi" 22 "github.com/ethereum/go-ethereum/common" 23 24 "bytes" 25 "os" 26 "regexp" 27 ) 28 29 type decodedArgument struct { 30 soltype abi.Argument 31 value interface{} 32 } 33 type decodedCallData struct { 34 signature string 35 name string 36 inputs []decodedArgument 37 } 38 39 //字符串实现Stringer接口,尝试使用基础值类型 40 func (arg decodedArgument) String() string { 41 var value string 42 switch val := arg.value.(type) { 43 case fmt.Stringer: 44 value = val.String() 45 default: 46 value = fmt.Sprintf("%v", val) 47 } 48 return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value) 49 } 50 51 //字符串实现用于decodedcalldata的字符串接口 52 func (cd decodedCallData) String() string { 53 args := make([]string, len(cd.inputs)) 54 for i, arg := range cd.inputs { 55 args[i] = arg.String() 56 } 57 return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ",")) 58 } 59 60 //ParseCallData将提供的调用数据与ABI定义匹配, 61 //并返回包含实际go类型值的结构 62 func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) { 63 64 if len(calldata) < 4 { 65 return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata)) 66 } 67 68 sigdata, argdata := calldata[:4], calldata[4:] 69 if len(argdata)%32 != 0 { 70 return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata)) 71 } 72 73 abispec, err := abi.JSON(strings.NewReader(abidata)) 74 if err != nil { 75 return nil, fmt.Errorf("Failed parsing JSON ABI: %v, abidata: %v", err, abidata) 76 } 77 78 method, err := abispec.MethodById(sigdata) 79 if err != nil { 80 return nil, err 81 } 82 83 v, err := method.Inputs.UnpackValues(argdata) 84 if err != nil { 85 return nil, err 86 } 87 88 decoded := decodedCallData{signature: method.Sig(), name: method.Name} 89 90 for n, argument := range method.Inputs { 91 if err != nil { 92 return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err) 93 } 94 decodedArg := decodedArgument{ 95 soltype: argument, 96 value: v[n], 97 } 98 decoded.inputs = append(decoded.inputs, decodedArg) 99 } 100 101 //数据解码完毕。此时,我们对解码后的数据进行编码,以查看它是否与 102 //原始数据。如果我们不这样做,就可以在参数中填充额外的数据,例如 103 //仅通过解码数据检测不到。 104 105 var ( 106 encoded []byte 107 ) 108 encoded, err = method.Inputs.PackValues(v) 109 110 if err != nil { 111 return nil, err 112 } 113 114 if !bytes.Equal(encoded, argdata) { 115 was := common.Bytes2Hex(encoded) 116 exp := common.Bytes2Hex(argdata) 117 return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig()) 118 } 119 return &decoded, nil 120 } 121 122 //methodSelectorToAbi将方法选择器转换为ABI结构。返回的数据是有效的JSON字符串 123 //可由标准ABI包装使用。 124 func MethodSelectorToAbi(selector string) ([]byte, error) { 125 126 re := regexp.MustCompile(`^([^\)]+)\(([a-z0-9,\[\]]*)\)`) 127 128 type fakeArg struct { 129 Type string `json:"type"` 130 } 131 type fakeABI struct { 132 Name string `json:"name"` 133 Type string `json:"type"` 134 Inputs []fakeArg `json:"inputs"` 135 } 136 groups := re.FindStringSubmatch(selector) 137 if len(groups) != 3 { 138 return nil, fmt.Errorf("Did not match: %v (%v matches)", selector, len(groups)) 139 } 140 name := groups[1] 141 args := groups[2] 142 arguments := make([]fakeArg, 0) 143 if len(args) > 0 { 144 for _, arg := range strings.Split(args, ",") { 145 arguments = append(arguments, fakeArg{arg}) 146 } 147 } 148 abicheat := fakeABI{ 149 name, "function", arguments, 150 } 151 return json.Marshal([]fakeABI{abicheat}) 152 153 } 154 155 type AbiDb struct { 156 db map[string]string 157 customdb map[string]string 158 customdbPath string 159 } 160 161 //出于测试目的,存在newEmptyBidb 162 func NewEmptyAbiDB() (*AbiDb, error) { 163 return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil 164 } 165 166 //newabidbfrmfile从文件加载签名数据库,以及 167 //如果文件不是有效的JSON,则会出错。没有其他内容验证 168 func NewAbiDBFromFile(path string) (*AbiDb, error) { 169 raw, err := ioutil.ReadFile(path) 170 if err != nil { 171 return nil, err 172 } 173 db, err := NewEmptyAbiDB() 174 if err != nil { 175 return nil, err 176 } 177 json.Unmarshal(raw, &db.db) 178 return db, nil 179 } 180 181 //newabidbfrmfiles同时加载标准签名数据库和自定义数据库。后者将被使用 182 //如果通过API提交新值,则将其写入 183 func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) { 184 185 db := &AbiDb{make(map[string]string), make(map[string]string), custom} 186 db.customdbPath = custom 187 188 raw, err := ioutil.ReadFile(standard) 189 if err != nil { 190 return nil, err 191 } 192 json.Unmarshal(raw, &db.db) 193 //自定义文件可能不存在。如果需要,将在保存期间创建 194 if _, err := os.Stat(custom); err == nil { 195 raw, err = ioutil.ReadFile(custom) 196 if err != nil { 197 return nil, err 198 } 199 json.Unmarshal(raw, &db.customdb) 200 } 201 202 return db, nil 203 } 204 205 //LookupMethodSelector对照已知的ABI方法检查给定的4字节序列。 206 //obs:此方法不验证匹配,假定调用方将验证匹配 207 func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) { 208 if len(id) < 4 { 209 return "", fmt.Errorf("Expected 4-byte id, got %d", len(id)) 210 } 211 sig := common.ToHex(id[:4]) 212 if key, exists := db.db[sig]; exists { 213 return key, nil 214 } 215 if key, exists := db.customdb[sig]; exists { 216 return key, nil 217 } 218 return "", fmt.Errorf("Signature %v not found", sig) 219 } 220 func (db *AbiDb) Size() int { 221 return len(db.db) 222 } 223 224 //savecustomabi临时保存签名。如果使用自定义文件,也将保存到磁盘 225 func (db *AbiDb) saveCustomAbi(selector, signature string) error { 226 db.customdb[signature] = selector 227 if db.customdbPath == "" { 228 return nil //本身不是错误,只是没有使用 229 } 230 d, err := json.Marshal(db.customdb) 231 if err != nil { 232 return err 233 } 234 err = ioutil.WriteFile(db.customdbPath, d, 0600) 235 return err 236 } 237 238 //如果启用了自定义数据库保存,则向数据库添加签名。 239 //OBS:这种方法不能验证数据的正确性, 240 //假定呼叫者已经这样做了 241 func (db *AbiDb) AddSignature(selector string, data []byte) error { 242 if len(data) < 4 { 243 return nil 244 } 245 _, err := db.LookupMethodSelector(data[:4]) 246 if err == nil { 247 return nil 248 } 249 sig := common.ToHex(data[:4]) 250 return db.saveCustomAbi(selector, sig) 251 } 252