github.com/amazechain/amc@v0.1.3/internal/api/filters/filter_query.go (about) 1 package filters 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "github.com/amazechain/amc/common/hexutil" 8 "github.com/amazechain/amc/common/types" 9 mvm_common "github.com/amazechain/amc/internal/avm/common" 10 mvm_types "github.com/amazechain/amc/internal/avm/types" 11 "github.com/amazechain/amc/modules/rpc/jsonrpc" 12 "math/big" 13 ) 14 15 // FilterCriteria contains options for contract log filtering. 16 type FilterCriteria struct { 17 BlockHash types.Hash // used by eth_getLogs, return logs only from block with this hash 18 FromBlock *big.Int // beginning of the queried range, nil means genesis block 19 ToBlock *big.Int // end of the rtypes nil means latest block 20 Addresses []types.Address // restricts matches to events created by specific contracts 21 22 // The Topic list restricts matches to particular event topics. Each event has a list 23 // of topics. Topics matches a prefix of that list. An empty element slice matches any 24 // topic. Non-empty elements represent an alternative that matches any of the 25 // contained topics. 26 // 27 // Examples: 28 // {} or nil matches any topic list 29 // {{A}} matches topic A in first position 30 // {{}, {B}} matches any topic in first position AND B in second position 31 // {{A}, {B}} matches topic A in first position AND B in second position 32 // {{A, B}, {C, D}} matches topic (A OR B) in first position AND (C OR D) in second position 33 Topics [][]types.Hash 34 } 35 36 // UnmarshalJSON sets *args fields with given data. 37 func (args *FilterCriteria) UnmarshalJSON(data []byte) error { 38 type input struct { 39 BlockHash *mvm_common.Hash `json:"blockHash"` 40 FromBlock *jsonrpc.BlockNumber `json:"fromBlock"` 41 ToBlock *jsonrpc.BlockNumber `json:"toBlock"` 42 Addresses interface{} `json:"address"` 43 Topics []interface{} `json:"topics"` 44 } 45 46 var raw input 47 if err := json.Unmarshal(data, &raw); err != nil { 48 return err 49 } 50 51 if raw.BlockHash != nil { 52 if raw.FromBlock != nil || raw.ToBlock != nil { 53 // BlockHash is mutually exclusive with FromBlock/ToBlock criteria 54 return fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock, choose one or the other") 55 } 56 args.BlockHash = mvm_types.ToAmcHash(*raw.BlockHash) 57 } else { 58 if raw.FromBlock != nil { 59 args.FromBlock = big.NewInt(raw.FromBlock.Int64()) 60 } 61 62 if raw.ToBlock != nil { 63 args.ToBlock = big.NewInt(raw.ToBlock.Int64()) 64 } 65 } 66 67 args.Addresses = []types.Address{} 68 69 if raw.Addresses != nil { 70 // raw.Address can contain a single address or an array of addresses 71 switch rawAddr := raw.Addresses.(type) { 72 case []interface{}: 73 for i, addr := range rawAddr { 74 if strAddr, ok := addr.(string); ok { 75 addr, err := decodeAddress(strAddr) 76 if err != nil { 77 return fmt.Errorf("invalid address at index %d: %v", i, err) 78 } 79 args.Addresses = append(args.Addresses, *mvm_types.ToAmcAddress(&addr)) 80 } else { 81 return fmt.Errorf("non-string address at index %d", i) 82 } 83 } 84 case string: 85 addr, err := decodeAddress(rawAddr) 86 if err != nil { 87 return fmt.Errorf("invalid address: %v", err) 88 } 89 args.Addresses = []types.Address{*mvm_types.ToAmcAddress(&addr)} 90 default: 91 return errors.New("invalid addresses in query") 92 } 93 } 94 95 // topics is an array consisting of strings and/or arrays of strings. 96 // JSON null values are converted to types.Hash{} and ignored by the filter manager. 97 if len(raw.Topics) > 0 { 98 args.Topics = make([][]types.Hash, len(raw.Topics)) 99 for i, t := range raw.Topics { 100 switch topic := t.(type) { 101 case nil: 102 // ignore topic when matching logs 103 104 case string: 105 // match specific topic 106 top, err := decodeTopic(topic) 107 if err != nil { 108 return err 109 } 110 args.Topics[i] = []types.Hash{mvm_types.ToAmcHash(top)} 111 112 case []interface{}: 113 // or case e.g. [null, "topic0", "topic1"] 114 for _, rawTopic := range topic { 115 if rawTopic == nil { 116 // null component, match all 117 args.Topics[i] = nil 118 break 119 } 120 if topic, ok := rawTopic.(string); ok { 121 parsed, err := decodeTopic(topic) 122 if err != nil { 123 return err 124 } 125 args.Topics[i] = append(args.Topics[i], mvm_types.ToAmcHash(parsed)) 126 } else { 127 return fmt.Errorf("invalid topic(s)") 128 } 129 } 130 default: 131 return fmt.Errorf("invalid topic(s)") 132 } 133 } 134 } 135 136 return nil 137 } 138 139 func decodeAddress(s string) (mvm_common.Address, error) { 140 b, err := hexutil.Decode(s) 141 if err == nil && len(b) != mvm_common.AddressLength { 142 err = fmt.Errorf("hex has invalid length %d after decoding; expected %d for address", len(b), mvm_common.AddressLength) 143 } 144 return mvm_common.BytesToAddress(b), err 145 } 146 147 func decodeTopic(s string) (mvm_common.Hash, error) { 148 b, err := hexutil.Decode(s) 149 if err == nil && len(b) != mvm_common.HashLength { 150 err = fmt.Errorf("hex has invalid length %d after decoding; expected %d for topic", len(b), mvm_common.HashLength) 151 } 152 return mvm_common.BytesToHash(b), err 153 }