github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/signer/core/abihelper.go (about) 1 2 package core 3 4 import ( 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "strings" 9 10 "github.com/quickchainproject/quickchain/accounts/abi" 11 "github.com/quickchainproject/quickchain/common" 12 13 "bytes" 14 "os" 15 "regexp" 16 ) 17 18 type decodedArgument struct { 19 soltype abi.Argument 20 value interface{} 21 } 22 type decodedCallData struct { 23 signature string 24 name string 25 inputs []decodedArgument 26 } 27 28 // String implements stringer interface, tries to use the underlying value-type 29 func (arg decodedArgument) String() string { 30 var value string 31 switch arg.value.(type) { 32 case fmt.Stringer: 33 value = arg.value.(fmt.Stringer).String() 34 default: 35 value = fmt.Sprintf("%v", arg.value) 36 } 37 return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value) 38 } 39 40 // String implements stringer interface for decodedCallData 41 func (cd decodedCallData) String() string { 42 args := make([]string, len(cd.inputs)) 43 for i, arg := range cd.inputs { 44 args[i] = arg.String() 45 } 46 return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ",")) 47 } 48 49 // parseCallData matches the provided call data against the abi definition, 50 // and returns a struct containing the actual go-typed values 51 func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) { 52 53 if len(calldata) < 4 { 54 return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata)) 55 } 56 57 sigdata, argdata := calldata[:4], calldata[4:] 58 if len(argdata)%32 != 0 { 59 return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata)) 60 } 61 62 abispec, err := abi.JSON(strings.NewReader(abidata)) 63 if err != nil { 64 return nil, fmt.Errorf("Failed parsing JSON ABI: %v, abidata: %v", err, abidata) 65 } 66 67 method, err := abispec.MethodById(sigdata) 68 if err != nil { 69 return nil, err 70 } 71 72 v, err := method.Inputs.UnpackValues(argdata) 73 if err != nil { 74 return nil, err 75 } 76 77 decoded := decodedCallData{signature: method.Sig(), name: method.Name} 78 79 for n, argument := range method.Inputs { 80 if err != nil { 81 return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err) 82 } else { 83 decodedArg := decodedArgument{ 84 soltype: argument, 85 value: v[n], 86 } 87 decoded.inputs = append(decoded.inputs, decodedArg) 88 } 89 } 90 91 // We're finished decoding the data. At this point, we encode the decoded data to see if it matches with the 92 // original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which 93 // is not detected by merely decoding the data. 94 95 var ( 96 encoded []byte 97 ) 98 encoded, err = method.Inputs.PackValues(v) 99 100 if err != nil { 101 return nil, err 102 } 103 104 if !bytes.Equal(encoded, argdata) { 105 was := common.Bytes2Hex(encoded) 106 exp := common.Bytes2Hex(argdata) 107 return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig()) 108 } 109 return &decoded, nil 110 } 111 112 // MethodSelectorToAbi converts a method selector into an ABI struct. The returned data is a valid json string 113 // which can be consumed by the standard abi package. 114 func MethodSelectorToAbi(selector string) ([]byte, error) { 115 116 re := regexp.MustCompile(`^([^\)]+)\(([a-z0-9,\[\]]*)\)`) 117 118 type fakeArg struct { 119 Type string `json:"type"` 120 } 121 type fakeABI struct { 122 Name string `json:"name"` 123 Type string `json:"type"` 124 Inputs []fakeArg `json:"inputs"` 125 } 126 groups := re.FindStringSubmatch(selector) 127 if len(groups) != 3 { 128 return nil, fmt.Errorf("Did not match: %v (%v matches)", selector, len(groups)) 129 } 130 name := groups[1] 131 args := groups[2] 132 arguments := make([]fakeArg, 0) 133 if len(args) > 0 { 134 for _, arg := range strings.Split(args, ",") { 135 arguments = append(arguments, fakeArg{arg}) 136 } 137 } 138 abicheat := fakeABI{ 139 name, "function", arguments, 140 } 141 return json.Marshal([]fakeABI{abicheat}) 142 143 } 144 145 type AbiDb struct { 146 db map[string]string 147 customdb map[string]string 148 customdbPath string 149 } 150 151 // NewEmptyAbiDB exists for test purposes 152 func NewEmptyAbiDB() (*AbiDb, error) { 153 return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil 154 } 155 156 // NewAbiDBFromFile loads signature database from file, and 157 // errors if the file is not valid json. Does no other validation of contents 158 func NewAbiDBFromFile(path string) (*AbiDb, error) { 159 raw, err := ioutil.ReadFile(path) 160 if err != nil { 161 return nil, err 162 } 163 db, err := NewEmptyAbiDB() 164 if err != nil { 165 return nil, err 166 } 167 json.Unmarshal(raw, &db.db) 168 return db, nil 169 } 170 171 // NewAbiDBFromFiles loads both the standard signature database and a custom database. The latter will be used 172 // to write new values into if they are submitted via the API 173 func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) { 174 175 db := &AbiDb{make(map[string]string), make(map[string]string), custom} 176 db.customdbPath = custom 177 178 raw, err := ioutil.ReadFile(standard) 179 if err != nil { 180 return nil, err 181 } 182 json.Unmarshal(raw, &db.db) 183 // Custom file may not exist. Will be created during save, if needed 184 if _, err := os.Stat(custom); err == nil { 185 raw, err = ioutil.ReadFile(custom) 186 if err != nil { 187 return nil, err 188 } 189 json.Unmarshal(raw, &db.customdb) 190 } 191 192 return db, nil 193 } 194 195 // LookupMethodSelector checks the given 4byte-sequence against the known ABI methods. 196 // OBS: This method does not validate the match, it's assumed the caller will do so 197 func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) { 198 if len(id) < 4 { 199 return "", fmt.Errorf("Expected 4-byte id, got %d", len(id)) 200 } 201 sig := common.ToHex(id[:4]) 202 if key, exists := db.db[sig]; exists { 203 return key, nil 204 } 205 if key, exists := db.customdb[sig]; exists { 206 return key, nil 207 } 208 return "", fmt.Errorf("Signature %v not found", sig) 209 } 210 func (db *AbiDb) Size() int { 211 return len(db.db) 212 } 213 214 // saveCustomAbi saves a signature ephemerally. If custom file is used, also saves to disk 215 func (db *AbiDb) saveCustomAbi(selector, signature string) error { 216 db.customdb[signature] = selector 217 if db.customdbPath == "" { 218 return nil //Not an error per se, just not used 219 } 220 d, err := json.Marshal(db.customdb) 221 if err != nil { 222 return err 223 } 224 err = ioutil.WriteFile(db.customdbPath, d, 0600) 225 return err 226 } 227 228 // Adds a signature to the database, if custom database saving is enabled. 229 // OBS: This method does _not_ validate the correctness of the data, 230 // it is assumed that the caller has already done so 231 func (db *AbiDb) AddSignature(selector string, data []byte) error { 232 if len(data) < 4 { 233 return nil 234 } 235 _, err := db.LookupMethodSelector(data[:4]) 236 if err == nil { 237 return nil 238 } 239 sig := common.ToHex(data[:4]) 240 return db.saveCustomAbi(selector, sig) 241 }