github.com/guiltylotus/go-ethereum@v1.9.7/accounts/abi/bind/topics.go (about) 1 // Copyright 2018 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 bind 18 19 import ( 20 "encoding/binary" 21 "errors" 22 "fmt" 23 "math/big" 24 "reflect" 25 26 "github.com/ethereum/go-ethereum/accounts/abi" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/crypto" 29 ) 30 31 // makeTopics converts a filter query argument list into a filter topic set. 32 func makeTopics(query ...[]interface{}) ([][]common.Hash, error) { 33 topics := make([][]common.Hash, len(query)) 34 for i, filter := range query { 35 for _, rule := range filter { 36 var topic common.Hash 37 38 // Try to generate the topic based on simple types 39 switch rule := rule.(type) { 40 case common.Hash: 41 copy(topic[:], rule[:]) 42 case common.Address: 43 copy(topic[common.HashLength-common.AddressLength:], rule[:]) 44 case *big.Int: 45 blob := rule.Bytes() 46 copy(topic[common.HashLength-len(blob):], blob) 47 case bool: 48 if rule { 49 topic[common.HashLength-1] = 1 50 } 51 case int8: 52 blob := big.NewInt(int64(rule)).Bytes() 53 copy(topic[common.HashLength-len(blob):], blob) 54 case int16: 55 blob := big.NewInt(int64(rule)).Bytes() 56 copy(topic[common.HashLength-len(blob):], blob) 57 case int32: 58 blob := big.NewInt(int64(rule)).Bytes() 59 copy(topic[common.HashLength-len(blob):], blob) 60 case int64: 61 blob := big.NewInt(rule).Bytes() 62 copy(topic[common.HashLength-len(blob):], blob) 63 case uint8: 64 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 65 copy(topic[common.HashLength-len(blob):], blob) 66 case uint16: 67 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 68 copy(topic[common.HashLength-len(blob):], blob) 69 case uint32: 70 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 71 copy(topic[common.HashLength-len(blob):], blob) 72 case uint64: 73 blob := new(big.Int).SetUint64(rule).Bytes() 74 copy(topic[common.HashLength-len(blob):], blob) 75 case string: 76 hash := crypto.Keccak256Hash([]byte(rule)) 77 copy(topic[:], hash[:]) 78 case []byte: 79 hash := crypto.Keccak256Hash(rule) 80 copy(topic[:], hash[:]) 81 82 default: 83 // todo(rjl493456442) according solidity documentation, indexed event 84 // parameters that are not value types i.e. arrays and structs are not 85 // stored directly but instead a keccak256-hash of an encoding is stored. 86 // 87 // We only convert stringS and bytes to hash, still need to deal with 88 // array(both fixed-size and dynamic-size) and struct. 89 90 // Attempt to generate the topic from funky types 91 val := reflect.ValueOf(rule) 92 switch { 93 // static byte array 94 case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: 95 reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val) 96 default: 97 return nil, fmt.Errorf("unsupported indexed type: %T", rule) 98 } 99 } 100 topics[i] = append(topics[i], topic) 101 } 102 } 103 return topics, nil 104 } 105 106 // Big batch of reflect types for topic reconstruction. 107 var ( 108 reflectHash = reflect.TypeOf(common.Hash{}) 109 reflectAddress = reflect.TypeOf(common.Address{}) 110 reflectBigInt = reflect.TypeOf(new(big.Int)) 111 ) 112 113 // parseTopics converts the indexed topic fields into actual log field values. 114 // 115 // Note, dynamic types cannot be reconstructed since they get mapped to Keccak256 116 // hashes as the topic value! 117 func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error { 118 // Sanity check that the fields and topics match up 119 if len(fields) != len(topics) { 120 return errors.New("topic/field count mismatch") 121 } 122 // Iterate over all the fields and reconstruct them from topics 123 for _, arg := range fields { 124 if !arg.Indexed { 125 return errors.New("non-indexed field in topic reconstruction") 126 } 127 field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name)) 128 129 // Try to parse the topic back into the fields based on primitive types 130 switch field.Kind() { 131 case reflect.Bool: 132 if topics[0][common.HashLength-1] == 1 { 133 field.Set(reflect.ValueOf(true)) 134 } 135 case reflect.Int8: 136 num := new(big.Int).SetBytes(topics[0][:]) 137 field.Set(reflect.ValueOf(int8(num.Int64()))) 138 139 case reflect.Int16: 140 num := new(big.Int).SetBytes(topics[0][:]) 141 field.Set(reflect.ValueOf(int16(num.Int64()))) 142 143 case reflect.Int32: 144 num := new(big.Int).SetBytes(topics[0][:]) 145 field.Set(reflect.ValueOf(int32(num.Int64()))) 146 147 case reflect.Int64: 148 num := new(big.Int).SetBytes(topics[0][:]) 149 field.Set(reflect.ValueOf(num.Int64())) 150 151 case reflect.Uint8: 152 num := new(big.Int).SetBytes(topics[0][:]) 153 field.Set(reflect.ValueOf(uint8(num.Uint64()))) 154 155 case reflect.Uint16: 156 num := new(big.Int).SetBytes(topics[0][:]) 157 field.Set(reflect.ValueOf(uint16(num.Uint64()))) 158 159 case reflect.Uint32: 160 num := new(big.Int).SetBytes(topics[0][:]) 161 field.Set(reflect.ValueOf(uint32(num.Uint64()))) 162 163 case reflect.Uint64: 164 num := new(big.Int).SetBytes(topics[0][:]) 165 field.Set(reflect.ValueOf(num.Uint64())) 166 167 default: 168 // Ran out of plain primitive types, try custom types 169 170 switch field.Type() { 171 case reflectHash: // Also covers all dynamic types 172 field.Set(reflect.ValueOf(topics[0])) 173 174 case reflectAddress: 175 var addr common.Address 176 copy(addr[:], topics[0][common.HashLength-common.AddressLength:]) 177 field.Set(reflect.ValueOf(addr)) 178 179 case reflectBigInt: 180 num := new(big.Int).SetBytes(topics[0][:]) 181 field.Set(reflect.ValueOf(num)) 182 183 default: 184 // Ran out of custom types, try the crazies 185 switch { 186 // static byte array 187 case arg.Type.T == abi.FixedBytesTy: 188 reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size])) 189 default: 190 return fmt.Errorf("unsupported indexed type: %v", arg.Type) 191 } 192 } 193 } 194 topics = topics[1:] 195 } 196 return nil 197 } 198 199 // parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs 200 func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error { 201 // Sanity check that the fields and topics match up 202 if len(fields) != len(topics) { 203 return errors.New("topic/field count mismatch") 204 } 205 // Iterate over all the fields and reconstruct them from topics 206 for _, arg := range fields { 207 if !arg.Indexed { 208 return errors.New("non-indexed field in topic reconstruction") 209 } 210 211 switch arg.Type.T { 212 case abi.BoolTy: 213 out[arg.Name] = topics[0][common.HashLength-1] == 1 214 case abi.IntTy, abi.UintTy: 215 num := new(big.Int).SetBytes(topics[0][:]) 216 out[arg.Name] = num 217 case abi.AddressTy: 218 var addr common.Address 219 copy(addr[:], topics[0][common.HashLength-common.AddressLength:]) 220 out[arg.Name] = addr 221 case abi.HashTy: 222 out[arg.Name] = topics[0] 223 case abi.FixedBytesTy: 224 out[arg.Name] = topics[0][:] 225 case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy: 226 // Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash 227 // whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash 228 out[arg.Name] = topics[0] 229 case abi.FunctionTy: 230 if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 { 231 return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes()) 232 } 233 var tmp [24]byte 234 copy(tmp[:], topics[0][8:32]) 235 out[arg.Name] = tmp 236 default: // Not handling tuples 237 return fmt.Errorf("unsupported indexed type: %v", arg.Type) 238 } 239 240 topics = topics[1:] 241 } 242 243 return nil 244 }