topics.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. package abi
  17. import (
  18. "encoding/binary"
  19. "errors"
  20. "fmt"
  21. "math/big"
  22. "reflect"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/crypto"
  25. )
  26. // MakeTopics converts a filter query argument list into a filter topic set.
  27. func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
  28. topics := make([][]common.Hash, len(query))
  29. for i, filter := range query {
  30. for _, rule := range filter {
  31. var topic common.Hash
  32. // Try to generate the topic based on simple types
  33. switch rule := rule.(type) {
  34. case common.Hash:
  35. copy(topic[:], rule[:])
  36. case common.Address:
  37. copy(topic[common.HashLength-common.AddressLength:], rule[:])
  38. case *big.Int:
  39. blob := rule.Bytes()
  40. copy(topic[common.HashLength-len(blob):], blob)
  41. case bool:
  42. if rule {
  43. topic[common.HashLength-1] = 1
  44. }
  45. case int8:
  46. copy(topic[:], genIntType(int64(rule), 1))
  47. case int16:
  48. copy(topic[:], genIntType(int64(rule), 2))
  49. case int32:
  50. copy(topic[:], genIntType(int64(rule), 4))
  51. case int64:
  52. copy(topic[:], genIntType(rule, 8))
  53. case uint8:
  54. blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
  55. copy(topic[common.HashLength-len(blob):], blob)
  56. case uint16:
  57. blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
  58. copy(topic[common.HashLength-len(blob):], blob)
  59. case uint32:
  60. blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
  61. copy(topic[common.HashLength-len(blob):], blob)
  62. case uint64:
  63. blob := new(big.Int).SetUint64(rule).Bytes()
  64. copy(topic[common.HashLength-len(blob):], blob)
  65. case string:
  66. hash := crypto.Keccak256Hash([]byte(rule))
  67. copy(topic[:], hash[:])
  68. case []byte:
  69. hash := crypto.Keccak256Hash(rule)
  70. copy(topic[:], hash[:])
  71. default:
  72. // todo(rjl493456442) according solidity documentation, indexed event
  73. // parameters that are not value types i.e. arrays and structs are not
  74. // stored directly but instead a keccak256-hash of an encoding is stored.
  75. //
  76. // We only convert stringS and bytes to hash, still need to deal with
  77. // array(both fixed-size and dynamic-size) and struct.
  78. // Attempt to generate the topic from funky types
  79. val := reflect.ValueOf(rule)
  80. switch {
  81. // static byte array
  82. case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
  83. reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
  84. default:
  85. return nil, fmt.Errorf("unsupported indexed type: %T", rule)
  86. }
  87. }
  88. topics[i] = append(topics[i], topic)
  89. }
  90. }
  91. return topics, nil
  92. }
  93. func genIntType(rule int64, size uint) []byte {
  94. var topic [common.HashLength]byte
  95. if rule < 0 {
  96. // if a rule is negative, we need to put it into two's complement.
  97. // extended to common.HashLength bytes.
  98. topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
  99. }
  100. for i := uint(0); i < size; i++ {
  101. topic[common.HashLength-i-1] = byte(rule >> (i * 8))
  102. }
  103. return topic[:]
  104. }
  105. // ParseTopics converts the indexed topic fields into actual log field values.
  106. func ParseTopics(out interface{}, fields Arguments, topics []common.Hash) error {
  107. return parseTopicWithSetter(fields, topics,
  108. func(arg Argument, reconstr interface{}) {
  109. field := reflect.ValueOf(out).Elem().FieldByName(ToCamelCase(arg.Name))
  110. field.Set(reflect.ValueOf(reconstr))
  111. })
  112. }
  113. // ParseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs.
  114. func ParseTopicsIntoMap(out map[string]interface{}, fields Arguments, topics []common.Hash) error {
  115. return parseTopicWithSetter(fields, topics,
  116. func(arg Argument, reconstr interface{}) {
  117. out[arg.Name] = reconstr
  118. })
  119. }
  120. // parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
  121. // provided set function.
  122. //
  123. // Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
  124. // hashes as the topic value!
  125. func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Argument, interface{})) error {
  126. // Sanity check that the fields and topics match up
  127. if len(fields) != len(topics) {
  128. return errors.New("topic/field count mismatch")
  129. }
  130. // Iterate over all the fields and reconstruct them from topics
  131. for i, arg := range fields {
  132. if !arg.Indexed {
  133. return errors.New("non-indexed field in topic reconstruction")
  134. }
  135. var reconstr interface{}
  136. switch arg.Type.T {
  137. case TupleTy:
  138. return errors.New("tuple type in topic reconstruction")
  139. case StringTy, BytesTy, SliceTy, ArrayTy:
  140. // Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
  141. // whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
  142. reconstr = topics[i]
  143. case FunctionTy:
  144. if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
  145. return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
  146. }
  147. var tmp [24]byte
  148. copy(tmp[:], topics[i][8:32])
  149. reconstr = tmp
  150. default:
  151. var err error
  152. reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
  153. if err != nil {
  154. return err
  155. }
  156. }
  157. // Use the setter function to store the value
  158. setter(arg, reconstr)
  159. }
  160. return nil
  161. }