github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/evm/abi/spec.go (about) 1 package abi 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "regexp" 7 "strconv" 8 "strings" 9 10 "github.com/hyperledger/burrow/crypto" 11 ) 12 13 // Token to use in deploy yaml in order to indicate call to the fallback function. 14 const FallbackFunctionName = "()" 15 16 // Spec is the ABI for contract decoded. 17 type Spec struct { 18 Constructor *FunctionSpec 19 Fallback *FunctionSpec 20 Functions map[string]*FunctionSpec 21 EventsByName map[string]*EventSpec 22 EventsByID map[EventID]*EventSpec 23 } 24 25 type specJSON struct { 26 Name string 27 Type string 28 Inputs []argumentJSON 29 Outputs []argumentJSON 30 StateMutability string 31 Anonymous bool 32 } 33 34 func NewSpec() *Spec { 35 return &Spec{ 36 // Zero value for constructor and fallback function is assumed when those functions are not present 37 Constructor: &FunctionSpec{}, 38 Fallback: &FunctionSpec{}, 39 EventsByName: make(map[string]*EventSpec), 40 EventsByID: make(map[EventID]*EventSpec), 41 Functions: make(map[string]*FunctionSpec), 42 } 43 } 44 45 // ReadSpec takes an ABI and decodes it for futher use 46 func ReadSpec(specBytes []byte) (*Spec, error) { 47 var specJ []specJSON 48 err := json.Unmarshal(specBytes, &specJ) 49 if err != nil { 50 // The abi spec file might a bin file, with the Abi under the Abi field in json 51 var binFile struct { 52 Abi []specJSON 53 } 54 err = json.Unmarshal(specBytes, &binFile) 55 if err != nil { 56 return nil, err 57 } 58 specJ = binFile.Abi 59 } 60 61 abiSpec := NewSpec() 62 63 for _, s := range specJ { 64 switch s.Type { 65 case "constructor": 66 abiSpec.Constructor.Inputs, err = readArgSpec(s.Inputs) 67 if err != nil { 68 return nil, err 69 } 70 case "fallback": 71 abiSpec.Fallback.Inputs = make([]Argument, 0) 72 abiSpec.Fallback.Outputs = make([]Argument, 0) 73 abiSpec.Fallback.SetConstant() 74 abiSpec.Functions[FallbackFunctionName] = abiSpec.Fallback 75 case "event": 76 ev := new(EventSpec) 77 err = ev.unmarshalSpec(&s) 78 if err != nil { 79 return nil, err 80 } 81 abiSpec.EventsByName[ev.Name] = ev 82 abiSpec.EventsByID[ev.ID] = ev 83 case "function": 84 inputs, err := readArgSpec(s.Inputs) 85 if err != nil { 86 return nil, err 87 } 88 outputs, err := readArgSpec(s.Outputs) 89 if err != nil { 90 return nil, err 91 } 92 abiSpec.Functions[s.Name] = NewFunctionSpec(s.Name, inputs, outputs).SetConstant() 93 } 94 } 95 96 return abiSpec, nil 97 } 98 99 // MergeSpec takes multiple Specs and merges them into once structure. Note that 100 // the same function name or event name can occur in different abis, so there might be 101 // some information loss. 102 func MergeSpec(abiSpec []*Spec) *Spec { 103 newSpec := NewSpec() 104 105 for _, s := range abiSpec { 106 for n, f := range s.Functions { 107 newSpec.Functions[n] = f 108 } 109 110 // Different Abis can have the Event name, but with a different signature 111 // Loop over the signatures, as these are less likely to have collisions 112 for _, e := range s.EventsByID { 113 newSpec.EventsByName[e.Name] = e 114 newSpec.EventsByID[e.ID] = e 115 } 116 } 117 118 return newSpec 119 } 120 121 func (spec *Spec) GetEventAbi(id EventID, addresses crypto.Address) (*EventSpec, error) { 122 eventSpec, ok := spec.EventsByID[id] 123 if !ok { 124 return nil, fmt.Errorf("could not find ABI for event with ID %v", id) 125 } 126 return eventSpec, nil 127 } 128 129 // Pack ABI encodes a function call. The fname specifies which function should called, if 130 // it doesn't exist exist the fallback function will be called. If fname is the empty 131 // string, the constructor is called. The arguments must be specified in args. The count 132 // must match the function being called. 133 // Returns the ABI encoded function call, whether the function is constant according 134 // to the ABI (which means it does not modified contract state) 135 func (spec *Spec) Pack(fname string, args ...interface{}) ([]byte, *FunctionSpec, error) { 136 var funcSpec *FunctionSpec 137 var argSpec []Argument 138 if fname != "" { 139 if _, ok := spec.Functions[fname]; ok { 140 funcSpec = spec.Functions[fname] 141 } else { 142 return nil, nil, fmt.Errorf("unknown function in Pack: %s", fname) 143 } 144 } else { 145 if spec.Constructor.Inputs != nil { 146 funcSpec = spec.Constructor 147 } else { 148 return nil, nil, fmt.Errorf("contract does not have a constructor") 149 } 150 } 151 152 argSpec = funcSpec.Inputs 153 154 packed := make([]byte, 0) 155 156 if fname != "" { 157 packed = funcSpec.FunctionID[:] 158 } 159 160 packedArgs, err := Pack(argSpec, args...) 161 if err != nil { 162 return nil, nil, err 163 } 164 165 return append(packed, packedArgs...), funcSpec, nil 166 } 167 168 // Unpack decodes the return values from a function call 169 func (spec *Spec) Unpack(data []byte, fname string, args ...interface{}) error { 170 var funcSpec *FunctionSpec 171 var argSpec []Argument 172 if fname != "" { 173 if _, ok := spec.Functions[fname]; ok { 174 funcSpec = spec.Functions[fname] 175 } else { 176 funcSpec = spec.Fallback 177 } 178 } else { 179 funcSpec = spec.Constructor 180 } 181 182 argSpec = funcSpec.Outputs 183 184 if argSpec == nil { 185 return fmt.Errorf("unknown function in Unpack: %s", fname) 186 } 187 188 return unpack(argSpec, data, func(i int) interface{} { 189 return args[i] 190 }) 191 } 192 193 func (spec *Spec) UnpackWithID(data []byte, args ...interface{}) error { 194 var argSpec []Argument 195 196 var id FunctionID 197 copy(id[:], data) 198 for _, fspec := range spec.Functions { 199 if id == fspec.FunctionID { 200 argSpec = fspec.Outputs 201 } 202 } 203 204 if argSpec == nil { 205 return fmt.Errorf("unknown function in UnpackWithID: %x", id) 206 } 207 208 return unpack(argSpec, data[4:], func(i int) interface{} { 209 return args[i] 210 }) 211 } 212 213 func readArgSpec(argsJ []argumentJSON) ([]Argument, error) { 214 args := make([]Argument, len(argsJ)) 215 var err error 216 217 for i, a := range argsJ { 218 args[i].Name = a.Name 219 args[i].Indexed = a.Indexed 220 221 baseType := a.Type 222 isArray := regexp.MustCompile(`(.*)\[([0-9]+)\]`) 223 m := isArray.FindStringSubmatch(a.Type) 224 if m != nil { 225 args[i].IsArray = true 226 args[i].ArrayLength, err = strconv.ParseUint(m[2], 10, 32) 227 if err != nil { 228 return nil, err 229 } 230 baseType = m[1] 231 } else if strings.HasSuffix(a.Type, "[]") { 232 args[i].IsArray = true 233 baseType = strings.TrimSuffix(a.Type, "[]") 234 } 235 236 isM := regexp.MustCompile("(bytes|uint|int)([0-9]+)") 237 m = isM.FindStringSubmatch(baseType) 238 if m != nil { 239 M, err := strconv.ParseUint(m[2], 10, 32) 240 if err != nil { 241 return nil, err 242 } 243 switch m[1] { 244 case "bytes": 245 if M < 1 || M > 32 { 246 return nil, fmt.Errorf("bytes%d is not valid type", M) 247 } 248 args[i].EVM = EVMBytes{M} 249 case "uint": 250 if M < 8 || M > 256 || (M%8) != 0 { 251 return nil, fmt.Errorf("uint%d is not valid type", M) 252 } 253 args[i].EVM = EVMUint{M} 254 case "int": 255 if M < 8 || M > 256 || (M%8) != 0 { 256 return nil, fmt.Errorf("uint%d is not valid type", M) 257 } 258 args[i].EVM = EVMInt{M} 259 } 260 continue 261 } 262 263 isMxN := regexp.MustCompile("(fixed|ufixed)([0-9]+)x([0-9]+)") 264 m = isMxN.FindStringSubmatch(baseType) 265 if m != nil { 266 M, err := strconv.ParseUint(m[2], 10, 32) 267 if err != nil { 268 return nil, err 269 } 270 N, err := strconv.ParseUint(m[3], 10, 32) 271 if err != nil { 272 return nil, err 273 } 274 if M < 8 || M > 256 || (M%8) != 0 { 275 return nil, fmt.Errorf("%s is not valid type", baseType) 276 } 277 if N == 0 || N > 80 { 278 return nil, fmt.Errorf("%s is not valid type", baseType) 279 } 280 if m[1] == "fixed" { 281 args[i].EVM = EVMFixed{N: N, M: M, signed: true} 282 } else if m[1] == "ufixed" { 283 args[i].EVM = EVMFixed{N: N, M: M, signed: false} 284 } else { 285 panic(m[1]) 286 } 287 continue 288 } 289 switch baseType { 290 case "uint": 291 args[i].EVM = EVMUint{M: 256} 292 case "int": 293 args[i].EVM = EVMInt{M: 256} 294 case "address": 295 args[i].EVM = EVMAddress{} 296 case "bool": 297 args[i].EVM = EVMBool{} 298 case "fixed": 299 args[i].EVM = EVMFixed{M: 128, N: 8, signed: true} 300 case "ufixed": 301 args[i].EVM = EVMFixed{M: 128, N: 8, signed: false} 302 case "bytes": 303 args[i].EVM = EVMBytes{M: 0} 304 case "string": 305 args[i].EVM = EVMString{} 306 default: 307 // Assume it is a type of Contract 308 args[i].EVM = EVMAddress{} 309 } 310 } 311 312 return args, nil 313 }