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