github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/evm/abi/abi.go (about) 1 package abi 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "math/big" 7 "os" 8 "path" 9 "path/filepath" 10 "reflect" 11 12 "github.com/hyperledger/burrow/crypto" 13 "github.com/hyperledger/burrow/deploy/compile" 14 "github.com/hyperledger/burrow/logging" 15 ) 16 17 // Variable exist to unpack return values into, so have both the return 18 // value and its name 19 type Variable struct { 20 Name string 21 Value string 22 } 23 24 // LoadPath loads one abi file or finds all files in a directory 25 func LoadPath(abiFileOrDirs ...string) (*Spec, error) { 26 if len(abiFileOrDirs) == 0 { 27 return nil, fmt.Errorf("no ABI file or directory provided") 28 } 29 30 specs := make([]*Spec, 0) 31 32 for _, dir := range abiFileOrDirs { 33 err := filepath.WalkDir(dir, func(path string, dir os.DirEntry, err error) error { 34 if err != nil { 35 return fmt.Errorf("error returned while walking abiDir '%s': %v", dir, err) 36 } 37 ext := filepath.Ext(path) 38 if dir.IsDir() || !(ext == ".bin" || ext == ".abi" || ext == ".json") { 39 return nil 40 } 41 abiSpc, err := ReadSpecFile(path) 42 if err != nil { 43 return fmt.Errorf("error parsing abi file at %s: %v", path, err) 44 } 45 specs = append(specs, abiSpc) 46 return nil 47 }) 48 if err != nil { 49 return nil, err 50 } 51 } 52 return MergeSpec(specs), nil 53 } 54 55 // EncodeFunctionCallFromFile ABI encodes a function call based on ABI in file, and the 56 // arguments specified as strings. 57 // The abiFileName specifies the name of the ABI file, and abiPath the path where it can be found. 58 // The fname specifies which function should called, if 59 // it doesn't exist exist the fallback function will be called. If fname is the empty 60 // string, the constructor is called. The arguments must be specified in args. The count 61 // must match the function being called. 62 // Returns the ABI encoded function call, whether the function is constant according 63 // to the ABI (which means it does not modified contract state) 64 func EncodeFunctionCallFromFile(abiFileName, abiPath, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) { 65 abiSpecBytes, err := readAbi(abiPath, abiFileName, logger) 66 if err != nil { 67 return []byte{}, nil, err 68 } 69 70 return EncodeFunctionCall(abiSpecBytes, funcName, logger, args...) 71 } 72 73 // EncodeFunctionCall ABI encodes a function call based on ABI in string abiData 74 // and the arguments specified as strings. 75 // The fname specifies which function should called, if 76 // it doesn't exist exist the fallback function will be called. If fname is the empty 77 // string, the constructor is called. The arguments must be specified in args. The count 78 // must match the function being called. 79 // Returns the ABI encoded function call, whether the function is constant according 80 // to the ABI (which means it does not modified contract state) 81 func EncodeFunctionCall(abiData, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) { 82 logger.TraceMsg("Packing Call via ABI", 83 "spec", abiData, 84 "function", funcName, 85 "arguments", fmt.Sprintf("%v", args), 86 ) 87 88 abiSpec, err := ReadSpec([]byte(abiData)) 89 if err != nil { 90 logger.InfoMsg("Failed to decode abi spec", 91 "abi", abiData, 92 "error", err.Error(), 93 ) 94 return nil, nil, err 95 } 96 97 packedBytes, funcSpec, err := abiSpec.Pack(funcName, args...) 98 if err != nil { 99 logger.InfoMsg("Failed to encode abi spec", 100 "abi", abiData, 101 "error", err.Error(), 102 ) 103 return nil, nil, err 104 } 105 106 return packedBytes, funcSpec, nil 107 } 108 109 // DecodeFunctionReturnFromFile ABI decodes the return value from a contract function call. 110 func DecodeFunctionReturnFromFile(abiLocation, binPath, funcName string, resultRaw []byte, logger *logging.Logger) ([]*Variable, error) { 111 abiSpecBytes, err := readAbi(binPath, abiLocation, logger) 112 if err != nil { 113 return nil, err 114 } 115 logger.TraceMsg("ABI Specification (Decode)", "spec", abiSpecBytes) 116 117 // Unpack the result 118 return DecodeFunctionReturn(abiSpecBytes, funcName, resultRaw) 119 } 120 121 func DecodeFunctionReturn(abiData, name string, data []byte) ([]*Variable, error) { 122 abiSpec, err := ReadSpec([]byte(abiData)) 123 if err != nil { 124 return nil, err 125 } 126 127 var args []Argument 128 129 if name == "" { 130 args = abiSpec.Constructor.Outputs 131 } else { 132 if _, ok := abiSpec.Functions[name]; ok { 133 args = abiSpec.Functions[name].Outputs 134 } else { 135 args = abiSpec.Fallback.Outputs 136 } 137 } 138 139 if args == nil { 140 return nil, fmt.Errorf("no such function") 141 } 142 vars := make([]*Variable, len(args)) 143 144 if len(args) == 0 { 145 return nil, nil 146 } 147 148 vals := make([]interface{}, len(args)) 149 for i := range vals { 150 vals[i] = new(string) 151 } 152 err = Unpack(args, data, vals...) 153 if err != nil { 154 return nil, err 155 } 156 157 for i, a := range args { 158 if a.Name != "" { 159 vars[i] = &Variable{Name: a.Name, Value: *(vals[i].(*string))} 160 } else { 161 vars[i] = &Variable{Name: fmt.Sprintf("%d", i), Value: *(vals[i].(*string))} 162 } 163 } 164 165 return vars, nil 166 } 167 168 // Spec 169 170 // ReadSpecFile reads an ABI file from a file 171 func ReadSpecFile(filename string) (*Spec, error) { 172 specBytes, err := ioutil.ReadFile(filename) 173 if err != nil { 174 return nil, err 175 } 176 177 return ReadSpec(specBytes) 178 } 179 180 // Struct reflection 181 182 // SpecFromStructReflect generates a FunctionSpec where the arguments and return values are 183 // described a struct. Both args and rets should be set to the return value of reflect.TypeOf() 184 // with the respective struct as an argument. 185 func SpecFromStructReflect(fname string, args reflect.Type, rets reflect.Type) *FunctionSpec { 186 inputs := make([]Argument, args.NumField()) 187 outputs := make([]Argument, rets.NumField()) 188 189 for i := 0; i < args.NumField(); i++ { 190 f := args.Field(i) 191 a := typeFromReflect(f.Type) 192 a.Name = f.Name 193 inputs[i] = a 194 } 195 196 for i := 0; i < rets.NumField(); i++ { 197 f := rets.Field(i) 198 a := typeFromReflect(f.Type) 199 a.Name = f.Name 200 outputs[i] = a 201 } 202 203 return NewFunctionSpec(fname, inputs, outputs) 204 } 205 206 func SpecFromFunctionReflect(fname string, v reflect.Value, skipIn, skipOut int) *FunctionSpec { 207 t := v.Type() 208 209 if t.Kind() != reflect.Func { 210 panic(fmt.Sprintf("%s is not a function", t.Name())) 211 } 212 213 inputs := make([]Argument, t.NumIn()-skipIn) 214 outputs := make([]Argument, t.NumOut()-skipOut) 215 216 for i := range inputs { 217 inputs[i] = typeFromReflect(t.In(i + skipIn)) 218 } 219 220 for i := range outputs { 221 outputs[i] = typeFromReflect(t.Out(i)) 222 } 223 224 return NewFunctionSpec(fname, inputs, outputs) 225 } 226 227 func GetPackingTypes(args []Argument) []interface{} { 228 res := make([]interface{}, len(args)) 229 230 for i, a := range args { 231 if a.IsArray { 232 t := reflect.TypeOf(a.EVM.getGoType()) 233 res[i] = reflect.MakeSlice(reflect.SliceOf(t), int(a.ArrayLength), 0).Interface() 234 } else { 235 res[i] = a.EVM.getGoType() 236 } 237 } 238 239 return res 240 } 241 242 func typeFromReflect(v reflect.Type) Argument { 243 arg := Argument{Name: v.Name()} 244 245 if v == reflect.TypeOf(crypto.Address{}) { 246 arg.EVM = EVMAddress{} 247 } else if v == reflect.TypeOf(big.Int{}) { 248 arg.EVM = EVMInt{M: 256} 249 } else { 250 if v.Kind() == reflect.Array { 251 arg.IsArray = true 252 arg.ArrayLength = uint64(v.Len()) 253 v = v.Elem() 254 } else if v.Kind() == reflect.Slice { 255 arg.IsArray = true 256 v = v.Elem() 257 } 258 259 switch v.Kind() { 260 case reflect.Bool: 261 arg.EVM = EVMBool{} 262 case reflect.String: 263 arg.EVM = EVMString{} 264 case reflect.Uint64: 265 arg.EVM = EVMUint{M: 64} 266 case reflect.Int64: 267 arg.EVM = EVMInt{M: 64} 268 default: 269 panic(fmt.Sprintf("no mapping for type %v", v.Kind())) 270 } 271 } 272 273 return arg 274 } 275 276 func readAbi(root, contract string, logger *logging.Logger) (string, error) { 277 p := path.Join(root, stripHex(contract)) 278 if _, err := os.Stat(p); err != nil { 279 logger.TraceMsg("abifile not found", "tried", p) 280 p = path.Join(root, stripHex(contract)+".bin") 281 if _, err = os.Stat(p); err != nil { 282 logger.TraceMsg("abifile not found", "tried", p) 283 return "", fmt.Errorf("abi doesn't exist for =>\t%s", p) 284 } 285 } 286 logger.TraceMsg("Found ABI file", "path", p) 287 sol, err := compile.LoadSolidityContract(p) 288 if err != nil { 289 return "", err 290 } 291 return string(sol.Abi), nil 292 } 293 294 func stripHex(s string) string { 295 if len(s) > 1 { 296 if s[:2] == "0x" { 297 s = s[2:] 298 if len(s)%2 != 0 { 299 s = "0" + s 300 } 301 return s 302 } 303 } 304 return s 305 }