github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/accounts/abi/abi.go (about) 1 package abi 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 ) 9 10 // The ABI holds information about a contract's context and available 11 // invokable methods. It will allow you to type check function calls and 12 // packs data accordingly. 13 type ABI struct { 14 Constructor Method 15 Methods map[string]Method 16 Events map[string]Event 17 } 18 19 // JSON returns a parsed ABI interface and error if it failed. 20 func JSON(reader io.Reader) (ABI, error) { 21 dec := json.NewDecoder(reader) 22 23 var abi ABI 24 if err := dec.Decode(&abi); err != nil { 25 return ABI{}, err 26 } 27 28 return abi, nil 29 } 30 31 // Pack the given method name to conform the ABI. Method call's data 32 // will consist of method_id, args0, arg1, ... argN. Method id consists 33 // of 4 bytes and arguments are all 32 bytes. 34 // Method ids are created from the first 4 bytes of the hash of the 35 // methods string signature. (signature = baz(uint32,string32)) 36 func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { 37 // Fetch the ABI of the requested method 38 if name == "" { 39 // constructor 40 arguments, err := abi.Constructor.Inputs.Pack(args...) 41 if err != nil { 42 return nil, err 43 } 44 return arguments, nil 45 46 } 47 method, exist := abi.Methods[name] 48 if !exist { 49 return nil, fmt.Errorf("method '%s' not found", name) 50 } 51 52 arguments, err := method.Inputs.Pack(args...) 53 if err != nil { 54 return nil, err 55 } 56 // Pack up the method ID too if not a constructor and return 57 return append(method.Id(), arguments...), nil 58 } 59 60 // Unpack output in v according to the abi specification 61 func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) { 62 if len(output) == 0 { 63 return fmt.Errorf("abi: unmarshalling empty output") 64 } 65 // since there can't be naming collisions with contracts and events, 66 // we need to decide whether we're calling a method or an event 67 if method, ok := abi.Methods[name]; ok { 68 if len(output)%32 != 0 { 69 return fmt.Errorf("abi: improperly formatted output") 70 } 71 return method.Outputs.Unpack(v, output) 72 } else if event, ok := abi.Events[name]; ok { 73 return event.Inputs.Unpack(v, output) 74 } 75 return fmt.Errorf("abi: could not locate named method or event") 76 } 77 78 // UnmarshalJSON implements json.Unmarshaler interface 79 func (abi *ABI) UnmarshalJSON(data []byte) error { 80 var fields []struct { 81 Type string 82 Name string 83 Constant bool 84 Anonymous bool 85 Inputs []Argument 86 Outputs []Argument 87 } 88 89 if err := json.Unmarshal(data, &fields); err != nil { 90 return err 91 } 92 93 abi.Methods = make(map[string]Method) 94 abi.Events = make(map[string]Event) 95 for _, field := range fields { 96 switch field.Type { 97 case "constructor": 98 abi.Constructor = Method{ 99 Inputs: field.Inputs, 100 } 101 // empty defaults to function according to the abi spec 102 case "function", "": 103 abi.Methods[field.Name] = Method{ 104 Name: field.Name, 105 Const: field.Constant, 106 Inputs: field.Inputs, 107 Outputs: field.Outputs, 108 } 109 case "event": 110 abi.Events[field.Name] = Event{ 111 Name: field.Name, 112 Anonymous: field.Anonymous, 113 Inputs: field.Inputs, 114 } 115 } 116 } 117 118 return nil 119 } 120 121 // MethodById looks up a method by the 4-byte id 122 // returns nil if none found 123 func (abi *ABI) MethodById(sigdata []byte) (*Method, error) { 124 for _, method := range abi.Methods { 125 if bytes.Equal(method.Id(), sigdata[:4]) { 126 return &method, nil 127 } 128 } 129 return nil, fmt.Errorf("no method with id: %#x", sigdata[:4]) 130 }