github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/abi/method.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum 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/ethereumproject/go-ethereum/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 (m Method) pack(method Method, 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 // Sig returns the methods string signature according to the ABI spec. 81 // 82 // Example 83 // 84 // function foo(uint32 a, int b) = "foo(uint32,int256)" 85 // 86 // Please note that "int" is substitute for its canonical representation "int256" 87 func (m Method) Sig() string { 88 types := make([]string, len(m.Inputs)) 89 i := 0 90 for _, input := range m.Inputs { 91 types[i] = input.Type.String() 92 i++ 93 } 94 return fmt.Sprintf("%v(%v)", m.Name, strings.Join(types, ",")) 95 } 96 97 func (m Method) String() string { 98 inputs := make([]string, len(m.Inputs)) 99 for i, input := range m.Inputs { 100 inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) 101 } 102 outputs := make([]string, len(m.Outputs)) 103 for i, output := range m.Outputs { 104 if len(output.Name) > 0 { 105 outputs[i] = fmt.Sprintf("%v ", output.Name) 106 } 107 outputs[i] += output.Type.String() 108 } 109 constant := "" 110 if m.Const { 111 constant = "constant " 112 } 113 return fmt.Sprintf("function %v(%v) %sreturns(%v)", m.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) 114 } 115 116 func (m Method) Id() []byte { 117 return crypto.Keccak256([]byte(m.Sig()))[:4] 118 }