github.com/Elemental-core/elementalcore@v0.0.0-20191206075037-63891242267a/accounts/abi/method.go (about) 1 // Copyright 2015 The elementalcore Authors 2 // This file is part of the elementalcore library. 3 // 4 // The elementalcore library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The elementalcore library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the elementalcore library. If not, see <http://www.gnu.org/licenses/>. 16 17 package abi 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 24 "github.com/Elemental-core/elementalcore/crypto" 25 ) 26 27 // Callable method given a `Name` and whether the method is a constant. 28 // If the method is `Const` no transaction needs to be created for this 29 // particular Method call. It can easily be simulated using a local VM. 30 // For example a `Balance()` method only needs to retrieve something 31 // from the storage and therefor requires no Tx to be send to the 32 // network. A method such as `Transact` does require a Tx and thus will 33 // be flagged `true`. 34 // Input specifies the required input parameters for this gives method. 35 type Method struct { 36 Name string 37 Const bool 38 Inputs []Argument 39 Outputs []Argument 40 } 41 42 func (method Method) pack(args ...interface{}) ([]byte, error) { 43 // Make sure arguments match up and pack them 44 if len(args) != len(method.Inputs) { 45 return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) 46 } 47 // variable input is the output appended at the end of packed 48 // output. This is used for strings and bytes types input. 49 var variableInput []byte 50 51 var ret []byte 52 for i, a := range args { 53 input := method.Inputs[i] 54 // pack the input 55 packed, err := input.Type.pack(reflect.ValueOf(a)) 56 if err != nil { 57 return nil, fmt.Errorf("`%s` %v", method.Name, err) 58 } 59 60 // check for a slice type (string, bytes, slice) 61 if input.Type.requiresLengthPrefix() { 62 // calculate the offset 63 offset := len(method.Inputs)*32 + len(variableInput) 64 // set the offset 65 ret = append(ret, packNum(reflect.ValueOf(offset))...) 66 // Append the packed output to the variable input. The variable input 67 // will be appended at the end of the input. 68 variableInput = append(variableInput, packed...) 69 } else { 70 // append the packed value to the input 71 ret = append(ret, packed...) 72 } 73 } 74 // append the variable input at the end of the packed input 75 ret = append(ret, variableInput...) 76 77 return ret, nil 78 } 79 80 // unpacks a method return tuple into a struct of corresponding go types 81 // 82 // Unpacking can be done into a struct or a slice/array. 83 func (method Method) tupleUnpack(v interface{}, output []byte) error { 84 // make sure the passed value is a pointer 85 valueOf := reflect.ValueOf(v) 86 if reflect.Ptr != valueOf.Kind() { 87 return fmt.Errorf("abi: Unpack(non-pointer %T)", v) 88 } 89 90 var ( 91 value = valueOf.Elem() 92 typ = value.Type() 93 ) 94 95 j := 0 96 for i := 0; i < len(method.Outputs); i++ { 97 toUnpack := method.Outputs[i] 98 if toUnpack.Type.T == ArrayTy { 99 // need to move this up because they read sequentially 100 j += toUnpack.Type.Size 101 } 102 marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output) 103 if err != nil { 104 return err 105 } 106 reflectValue := reflect.ValueOf(marshalledValue) 107 108 switch value.Kind() { 109 case reflect.Struct: 110 for j := 0; j < typ.NumField(); j++ { 111 field := typ.Field(j) 112 // TODO read tags: `abi:"fieldName"` 113 if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] { 114 if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil { 115 return err 116 } 117 } 118 } 119 case reflect.Slice, reflect.Array: 120 if value.Len() < i { 121 return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(method.Outputs), value.Len()) 122 } 123 v := value.Index(i) 124 if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface { 125 return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type()) 126 } 127 reflectValue := reflect.ValueOf(marshalledValue) 128 if err := set(v.Elem(), reflectValue, method.Outputs[i]); err != nil { 129 return err 130 } 131 default: 132 return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ) 133 } 134 } 135 return nil 136 } 137 138 func (method Method) isTupleReturn() bool { return len(method.Outputs) > 1 } 139 140 func (method Method) singleUnpack(v interface{}, output []byte) error { 141 // make sure the passed value is a pointer 142 valueOf := reflect.ValueOf(v) 143 if reflect.Ptr != valueOf.Kind() { 144 return fmt.Errorf("abi: Unpack(non-pointer %T)", v) 145 } 146 147 value := valueOf.Elem() 148 149 marshalledValue, err := toGoType(0, method.Outputs[0].Type, output) 150 if err != nil { 151 return err 152 } 153 if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil { 154 return err 155 } 156 return nil 157 } 158 159 // Sig returns the methods string signature according to the ABI spec. 160 // 161 // Example 162 // 163 // function foo(uint32 a, int b) = "foo(uint32,int256)" 164 // 165 // Please note that "int" is substitute for its canonical representation "int256" 166 func (m Method) Sig() string { 167 types := make([]string, len(m.Inputs)) 168 i := 0 169 for _, input := range m.Inputs { 170 types[i] = input.Type.String() 171 i++ 172 } 173 return fmt.Sprintf("%v(%v)", m.Name, strings.Join(types, ",")) 174 } 175 176 func (m Method) String() string { 177 inputs := make([]string, len(m.Inputs)) 178 for i, input := range m.Inputs { 179 inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) 180 } 181 outputs := make([]string, len(m.Outputs)) 182 for i, output := range m.Outputs { 183 if len(output.Name) > 0 { 184 outputs[i] = fmt.Sprintf("%v ", output.Name) 185 } 186 outputs[i] += output.Type.String() 187 } 188 constant := "" 189 if m.Const { 190 constant = "constant " 191 } 192 return fmt.Sprintf("function %v(%v) %sreturns(%v)", m.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) 193 } 194 195 func (m Method) Id() []byte { 196 return crypto.Keccak256([]byte(m.Sig()))[:4] 197 }