github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/whisper/topic.go (about) 1 // Copyright 2015 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 // Contains the Whisper protocol Topic element. For formal details please see 18 // the specs at https://github.com/ethereum/wiki/wiki/Whisper-PoC-1-Protocol-Spec#topics. 19 20 package whisper 21 22 import "github.com/ethereumproject/go-ethereum/crypto" 23 24 // Topic represents a cryptographically secure, probabilistic partial 25 // classifications of a message, determined as the first (left) 4 bytes of the 26 // SHA3 hash of some arbitrary data given by the original author of the message. 27 type Topic [4]byte 28 29 // NewTopic creates a topic from the 4 byte prefix of the SHA3 hash of the data. 30 // 31 // Note, empty topics are considered the wildcard, and cannot be used in messages. 32 func NewTopic(data []byte) Topic { 33 prefix := [4]byte{} 34 copy(prefix[:], crypto.Keccak256(data)[:4]) 35 return Topic(prefix) 36 } 37 38 // NewTopics creates a list of topics from a list of binary data elements, by 39 // iteratively calling NewTopic on each of them. 40 func NewTopics(data ...[]byte) []Topic { 41 topics := make([]Topic, len(data)) 42 for i, element := range data { 43 topics[i] = NewTopic(element) 44 } 45 return topics 46 } 47 48 // String converts a topic byte array to a string representation. 49 func (self *Topic) String() string { 50 return string(self[:]) 51 } 52 53 // topicMatcher is a filter expression to verify if a list of topics contained 54 // in an arriving message matches some topic conditions. The topic matcher is 55 // built up of a list of conditions, each of which must be satisfied by the 56 // corresponding topic in the message. Each condition may require: a) an exact 57 // topic match; b) a match from a set of topics; or c) a wild-card matching all. 58 // 59 // If a message contains more topics than required by the matcher, those beyond 60 // the condition count are ignored and assumed to match. 61 // 62 // Consider the following sample topic matcher: 63 // sample := { 64 // {TopicA1, TopicA2, TopicA3}, 65 // {TopicB}, 66 // nil, 67 // {TopicD1, TopicD2} 68 // } 69 // In order for a message to pass this filter, it should enumerate at least 4 70 // topics, the first any of [TopicA1, TopicA2, TopicA3], the second mandatory 71 // "TopicB", the third is ignored by the filter and the fourth either "TopicD1" 72 // or "TopicD2". If the message contains further topics, the filter will match 73 // them too. 74 type topicMatcher struct { 75 conditions []map[Topic]struct{} 76 } 77 78 // newTopicMatcher create a topic matcher from a list of topic conditions. 79 func newTopicMatcher(topics ...[]Topic) *topicMatcher { 80 matcher := make([]map[Topic]struct{}, len(topics)) 81 for i, condition := range topics { 82 matcher[i] = make(map[Topic]struct{}) 83 for _, topic := range condition { 84 matcher[i][topic] = struct{}{} 85 } 86 } 87 return &topicMatcher{conditions: matcher} 88 } 89 90 // Matches checks if a list of topics matches this particular condition set. 91 func (self *topicMatcher) Matches(topics []Topic) bool { 92 // Mismatch if there aren't enough topics 93 if len(self.conditions) > len(topics) { 94 return false 95 } 96 // Check each topic condition for existence (skip wild-cards) 97 for i := 0; i < len(topics) && i < len(self.conditions); i++ { 98 if len(self.conditions[i]) > 0 { 99 if _, ok := self.conditions[i][topics[i]]; !ok { 100 return false 101 } 102 } 103 } 104 return true 105 }