github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/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 "errors" 21 "fmt" 22 "math/big" 23 "reflect" 24 25 "github.com/ethereum/go-ethereum/accounts/abi" 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/crypto" 28 ) 29 30 // makeTopics converts a filter query argument list into a filter topic set. 31 func makeTopics(query ...[]interface{}) ([][]common.Hash, error) { 32 topics := make([][]common.Hash, len(query)) 33 for i, filter := range query { 34 for _, rule := range filter { 35 var topic common.Hash 36 37 // Try to generate the topic based on simple types 38 switch rule := rule.(type) { 39 case common.Hash: 40 copy(topic[:], rule[:]) 41 case common.Address: 42 copy(topic[common.HashLength-common.AddressLength:], rule[:]) 43 case *big.Int: 44 blob := rule.Bytes() 45 copy(topic[common.HashLength-len(blob):], blob) 46 case bool: 47 if rule { 48 topic[common.HashLength-1] = 1 49 } 50 case int8: 51 blob := big.NewInt(int64(rule)).Bytes() 52 copy(topic[common.HashLength-len(blob):], blob) 53 case int16: 54 blob := big.NewInt(int64(rule)).Bytes() 55 copy(topic[common.HashLength-len(blob):], blob) 56 case int32: 57 blob := big.NewInt(int64(rule)).Bytes() 58 copy(topic[common.HashLength-len(blob):], blob) 59 case int64: 60 blob := big.NewInt(rule).Bytes() 61 copy(topic[common.HashLength-len(blob):], blob) 62 case uint8: 63 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 64 copy(topic[common.HashLength-len(blob):], blob) 65 case uint16: 66 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 67 copy(topic[common.HashLength-len(blob):], blob) 68 case uint32: 69 blob := new(big.Int).SetUint64(uint64(rule)).Bytes() 70 copy(topic[common.HashLength-len(blob):], blob) 71 case uint64: 72 blob := new(big.Int).SetUint64(rule).Bytes() 73 copy(topic[common.HashLength-len(blob):], blob) 74 case string: 75 hash := crypto.Keccak256Hash([]byte(rule)) 76 copy(topic[:], hash[:]) 77 case []byte: 78 hash := crypto.Keccak256Hash(rule) 79 copy(topic[:], hash[:]) 80 81 default: 82 // Attempt to generate the topic from funky types 83 val := reflect.ValueOf(rule) 84 85 switch { 86 87 // static byte array 88 case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: 89 reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val) 90 91 default: 92 return nil, fmt.Errorf("unsupported indexed type: %T", rule) 93 } 94 } 95 topics[i] = append(topics[i], topic) 96 } 97 } 98 return topics, nil 99 } 100 101 // Big batch of reflect types for topic reconstruction. 102 var ( 103 reflectHash = reflect.TypeOf(common.Hash{}) 104 reflectAddress = reflect.TypeOf(common.Address{}) 105 reflectBigInt = reflect.TypeOf(new(big.Int)) 106 ) 107 108 // parseTopics converts the indexed topic fields into actual log field values. 109 // 110 // Note, dynamic types cannot be reconstructed since they get mapped to Keccak256 111 // hashes as the topic value! 112 func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error { 113 // Sanity check that the fields and topics match up 114 if len(fields) != len(topics) { 115 return errors.New("topic/field count mismatch") 116 } 117 // Iterate over all the fields and reconstruct them from topics 118 for _, arg := range fields { 119 if !arg.Indexed { 120 return errors.New("non-indexed field in topic reconstruction") 121 } 122 field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name)) 123 124 // Try to parse the topic back into the fields based on primitive types 125 switch field.Kind() { 126 case reflect.Bool: 127 if topics[0][common.HashLength-1] == 1 { 128 field.Set(reflect.ValueOf(true)) 129 } 130 case reflect.Int8: 131 num := new(big.Int).SetBytes(topics[0][:]) 132 field.Set(reflect.ValueOf(int8(num.Int64()))) 133 134 case reflect.Int16: 135 num := new(big.Int).SetBytes(topics[0][:]) 136 field.Set(reflect.ValueOf(int16(num.Int64()))) 137 138 case reflect.Int32: 139 num := new(big.Int).SetBytes(topics[0][:]) 140 field.Set(reflect.ValueOf(int32(num.Int64()))) 141 142 case reflect.Int64: 143 num := new(big.Int).SetBytes(topics[0][:]) 144 field.Set(reflect.ValueOf(num.Int64())) 145 146 case reflect.Uint8: 147 num := new(big.Int).SetBytes(topics[0][:]) 148 field.Set(reflect.ValueOf(uint8(num.Uint64()))) 149 150 case reflect.Uint16: 151 num := new(big.Int).SetBytes(topics[0][:]) 152 field.Set(reflect.ValueOf(uint16(num.Uint64()))) 153 154 case reflect.Uint32: 155 num := new(big.Int).SetBytes(topics[0][:]) 156 field.Set(reflect.ValueOf(uint32(num.Uint64()))) 157 158 case reflect.Uint64: 159 num := new(big.Int).SetBytes(topics[0][:]) 160 field.Set(reflect.ValueOf(num.Uint64())) 161 162 default: 163 // Ran out of plain primitive types, try custom types 164 switch field.Type() { 165 case reflectHash: // Also covers all dynamic types 166 field.Set(reflect.ValueOf(topics[0])) 167 168 case reflectAddress: 169 var addr common.Address 170 copy(addr[:], topics[0][common.HashLength-common.AddressLength:]) 171 field.Set(reflect.ValueOf(addr)) 172 173 case reflectBigInt: 174 num := new(big.Int).SetBytes(topics[0][:]) 175 field.Set(reflect.ValueOf(num)) 176 177 default: 178 // Ran out of custom types, try the crazies 179 switch { 180 181 // static byte array 182 case arg.Type.T == abi.FixedBytesTy: 183 reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size])) 184 185 default: 186 return fmt.Errorf("unsupported indexed type: %v", arg.Type) 187 } 188 } 189 } 190 topics = topics[1:] 191 } 192 return nil 193 }