github.com/datachainlab/burrow@v0.25.0/execution/evm/abi/core.go (about) 1 package abi 2 3 import ( 4 "fmt" 5 "os" 6 "path" 7 "path/filepath" 8 9 "github.com/hyperledger/burrow/deploy/compile" 10 "github.com/hyperledger/burrow/execution/errors" 11 "github.com/hyperledger/burrow/logging" 12 ) 13 14 // Variable exist to unpack return values into, so have both the return 15 // value and its name 16 type Variable struct { 17 Name string 18 Value string 19 } 20 21 func init() { 22 var err error 23 RevertAbi, err = ReadAbiSpec([]byte(`[{"name":"Error","type":"function","outputs":[{"type":"string"}],"inputs":[{"type":"string"}]}]`)) 24 if err != nil { 25 panic(fmt.Sprintf("internal error: failed to build revert abi: %v", err)) 26 } 27 } 28 29 // RevertAbi exists to decode reverts. Any contract function call fail using revert(), assert() or require(). 30 // If a function exits this way, the this hardcoded ABI will be used. 31 var RevertAbi *AbiSpec 32 33 // EncodeFunctionCallFromFile ABI encodes a function call based on ABI in file, and the 34 // arguments specified as strings. 35 // The abiFileName specifies the name of the ABI file, and abiPath the path where it can be found. 36 // The fname specifies which function should called, if 37 // it doesn't exist exist the fallback function will be called. If fname is the empty 38 // string, the constructor is called. The arguments must be specified in args. The count 39 // must match the function being called. 40 // Returns the ABI encoded function call, whether the function is constant according 41 // to the ABI (which means it does not modified contract state) 42 func EncodeFunctionCallFromFile(abiFileName, abiPath, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) { 43 abiSpecBytes, err := readAbi(abiPath, abiFileName, logger) 44 if err != nil { 45 return []byte{}, nil, err 46 } 47 48 return EncodeFunctionCall(abiSpecBytes, funcName, logger, args...) 49 } 50 51 // EncodeFunctionCall ABI encodes a function call based on ABI in string abiData 52 // and the arguments specified as strings. 53 // The fname specifies which function should called, if 54 // it doesn't exist exist the fallback function will be called. If fname is the empty 55 // string, the constructor is called. The arguments must be specified in args. The count 56 // must match the function being called. 57 // Returns the ABI encoded function call, whether the function is constant according 58 // to the ABI (which means it does not modified contract state) 59 func EncodeFunctionCall(abiData, funcName string, logger *logging.Logger, args ...interface{}) ([]byte, *FunctionSpec, error) { 60 logger.TraceMsg("Packing Call via ABI", 61 "spec", abiData, 62 "function", funcName, 63 "arguments", fmt.Sprintf("%v", args), 64 ) 65 66 abiSpec, err := ReadAbiSpec([]byte(abiData)) 67 if err != nil { 68 logger.InfoMsg("Failed to decode abi spec", 69 "abi", abiData, 70 "error", err.Error(), 71 ) 72 return nil, nil, err 73 } 74 75 packedBytes, funcSpec, err := abiSpec.Pack(funcName, args...) 76 if err != nil { 77 logger.InfoMsg("Failed to encode abi spec", 78 "abi", abiData, 79 "error", err.Error(), 80 ) 81 return nil, nil, err 82 } 83 84 return packedBytes, funcSpec, nil 85 } 86 87 // DecodeFunctionReturnFromFile ABI decodes the return value from a contract function call. 88 func DecodeFunctionReturnFromFile(abiLocation, binPath, funcName string, resultRaw []byte, logger *logging.Logger) ([]*Variable, error) { 89 abiSpecBytes, err := readAbi(binPath, abiLocation, logger) 90 if err != nil { 91 return nil, err 92 } 93 logger.TraceMsg("ABI Specification (Decode)", "spec", abiSpecBytes) 94 95 // Unpack the result 96 return DecodeFunctionReturn(abiSpecBytes, funcName, resultRaw) 97 } 98 99 func DecodeFunctionReturn(abiData, name string, data []byte) ([]*Variable, error) { 100 abiSpec, err := ReadAbiSpec([]byte(abiData)) 101 if err != nil { 102 return nil, err 103 } 104 105 var args []Argument 106 107 if name == "" { 108 args = abiSpec.Constructor.Outputs 109 } else { 110 if _, ok := abiSpec.Functions[name]; ok { 111 args = abiSpec.Functions[name].Outputs 112 } else { 113 args = abiSpec.Fallback.Outputs 114 } 115 } 116 117 if args == nil { 118 return nil, fmt.Errorf("no such function") 119 } 120 vars := make([]*Variable, len(args)) 121 122 if len(args) == 0 { 123 return nil, nil 124 } 125 126 vals := make([]interface{}, len(args)) 127 for i := range vals { 128 vals[i] = new(string) 129 } 130 err = Unpack(args, data, vals...) 131 if err != nil { 132 return nil, err 133 } 134 135 for i, a := range args { 136 if a.Name != "" { 137 vars[i] = &Variable{Name: a.Name, Value: *(vals[i].(*string))} 138 } else { 139 vars[i] = &Variable{Name: fmt.Sprintf("%d", i), Value: *(vals[i].(*string))} 140 } 141 } 142 143 return vars, nil 144 } 145 146 func readAbi(root, contract string, logger *logging.Logger) (string, error) { 147 p := path.Join(root, stripHex(contract)) 148 if _, err := os.Stat(p); err != nil { 149 logger.TraceMsg("abifile not found", "tried", p) 150 p = path.Join(root, stripHex(contract)+".bin") 151 if _, err = os.Stat(p); err != nil { 152 logger.TraceMsg("abifile not found", "tried", p) 153 return "", fmt.Errorf("Abi doesn't exist for =>\t%s", p) 154 } 155 } 156 logger.TraceMsg("Found ABI file", "path", p) 157 sol, err := compile.LoadSolidityContract(p) 158 if err != nil { 159 return "", err 160 } 161 return string(sol.Abi), nil 162 } 163 164 // LoadPath loads one abi file or finds all files in a directory 165 func LoadPath(abiFileOrDirs ...string) (*AbiSpec, error) { 166 if len(abiFileOrDirs) == 0 { 167 return &AbiSpec{}, fmt.Errorf("no ABI file or directory provided") 168 } 169 170 specs := make([]*AbiSpec, 0) 171 172 for _, dir := range abiFileOrDirs { 173 err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 174 if err != nil { 175 return fmt.Errorf("error returned while walking abiDir '%s': %v", dir, err) 176 } 177 ext := filepath.Ext(path) 178 if fi.IsDir() || !(ext == ".bin" || ext == ".abi") { 179 return nil 180 } 181 if err == nil { 182 abiSpc, err := ReadAbiSpecFile(path) 183 if err != nil { 184 return errors.Wrap(err, "Error parsing abi file "+path) 185 } 186 specs = append(specs, abiSpc) 187 } 188 return nil 189 }) 190 if err != nil { 191 return &AbiSpec{}, err 192 } 193 } 194 return MergeAbiSpec(specs), nil 195 } 196 197 func stripHex(s string) string { 198 if len(s) > 1 { 199 if s[:2] == "0x" { 200 s = s[2:] 201 if len(s)%2 != 0 { 202 s = "0" + s 203 } 204 return s 205 } 206 } 207 return s 208 }