github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/accounts/abi/abi.go (about) 1 package abi 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "strings" 8 9 "github.com/jonasnick/go-ethereum/crypto" 10 ) 11 12 // Callable method given a `Name` and whether the method is a constant. 13 // If the method is `Const` no transaction needs to be created for this 14 // particular Method call. It can easily be simulated using a local VM. 15 // For example a `Balance()` method only needs to retrieve something 16 // from the storage and therefor requires no Tx to be send to the 17 // network. A method such as `Transact` does require a Tx and thus will 18 // be flagged `true`. 19 // Input specifies the required input parameters for this gives method. 20 type Method struct { 21 Name string 22 Const bool 23 Input []Argument 24 Return Type // not yet implemented 25 } 26 27 // Returns the methods string signature according to the ABI spec. 28 // 29 // Example 30 // 31 // function foo(uint32 a, int b) = "foo(uint32,int256)" 32 // 33 // Please note that "int" is substitute for its canonical representation "int256" 34 func (m Method) String() (out string) { 35 out += m.Name 36 types := make([]string, len(m.Input)) 37 i := 0 38 for _, input := range m.Input { 39 types[i] = input.Type.String() 40 i++ 41 } 42 out += "(" + strings.Join(types, ",") + ")" 43 44 return 45 } 46 47 func (m Method) Id() []byte { 48 return crypto.Sha3([]byte(m.String()))[:4] 49 } 50 51 // Argument holds the name of the argument and the corresponding type. 52 // Types are used when packing and testing arguments. 53 type Argument struct { 54 Name string 55 Type Type 56 } 57 58 func (a *Argument) UnmarshalJSON(data []byte) error { 59 var extarg struct { 60 Name string 61 Type string 62 } 63 err := json.Unmarshal(data, &extarg) 64 if err != nil { 65 return fmt.Errorf("argument json err: %v", err) 66 } 67 68 a.Type, err = NewType(extarg.Type) 69 if err != nil { 70 return err 71 } 72 a.Name = extarg.Name 73 74 return nil 75 } 76 77 // The ABI holds information about a contract's context and available 78 // invokable methods. It will allow you to type check function calls and 79 // packs data accordingly. 80 type ABI struct { 81 Methods map[string]Method 82 } 83 84 // tests, tests whether the given input would result in a successful 85 // call. Checks argument list count and matches input to `input`. 86 func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) { 87 method := abi.Methods[name] 88 89 var ret []byte 90 for i, a := range args { 91 input := method.Input[i] 92 93 packed, err := input.Type.pack(a) 94 if err != nil { 95 return nil, fmt.Errorf("`%s` %v", name, err) 96 } 97 ret = append(ret, packed...) 98 99 } 100 101 return ret, nil 102 } 103 104 // Pack the given method name to conform the ABI. Method call's data 105 // will consist of method_id, args0, arg1, ... argN. Method id consists 106 // of 4 bytes and arguments are all 32 bytes. 107 // Method ids are created from the first 4 bytes of the hash of the 108 // methods string signature. (signature = baz(uint32,string32)) 109 func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { 110 method, exist := abi.Methods[name] 111 if !exist { 112 return nil, fmt.Errorf("method '%s' not found", name) 113 } 114 115 // start with argument count match 116 if len(args) != len(method.Input) { 117 return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input)) 118 } 119 120 arguments, err := abi.pack(name, args...) 121 if err != nil { 122 return nil, err 123 } 124 125 // Set function id 126 packed := abi.Methods[name].Id() 127 packed = append(packed, arguments...) 128 129 return packed, nil 130 } 131 132 func (abi *ABI) UnmarshalJSON(data []byte) error { 133 var methods []Method 134 if err := json.Unmarshal(data, &methods); err != nil { 135 return err 136 } 137 138 abi.Methods = make(map[string]Method) 139 for _, method := range methods { 140 abi.Methods[method.Name] = method 141 } 142 143 return nil 144 } 145 146 func JSON(reader io.Reader) (ABI, error) { 147 dec := json.NewDecoder(reader) 148 149 var abi ABI 150 if err := dec.Decode(&abi); err != nil { 151 return ABI{}, err 152 } 153 154 return abi, nil 155 }