github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/topic.go (about) 1 // Copyright 2015 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 whisperv2 21 22 import "github.com/SmartMeshFoundation/Spectrum/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 // NewTopicFromString creates a topic using the binary data contents of the 49 // specified string. 50 func NewTopicFromString(data string) Topic { 51 return NewTopic([]byte(data)) 52 } 53 54 // NewTopicsFromStrings creates a list of topics from a list of textual data 55 // elements, by iteratively calling NewTopicFromString on each of them. 56 func NewTopicsFromStrings(data ...string) []Topic { 57 topics := make([]Topic, len(data)) 58 for i, element := range data { 59 topics[i] = NewTopicFromString(element) 60 } 61 return topics 62 } 63 64 // String converts a topic byte array to a string representation. 65 func (self *Topic) String() string { 66 return string(self[:]) 67 } 68 69 // topicMatcher is a filter expression to verify if a list of topics contained 70 // in an arriving message matches some topic conditions. The topic matcher is 71 // built up of a list of conditions, each of which must be satisfied by the 72 // corresponding topic in the message. Each condition may require: a) an exact 73 // topic match; b) a match from a set of topics; or c) a wild-card matching all. 74 // 75 // If a message contains more topics than required by the matcher, those beyond 76 // the condition count are ignored and assumed to match. 77 // 78 // Consider the following sample topic matcher: 79 // sample := { 80 // {TopicA1, TopicA2, TopicA3}, 81 // {TopicB}, 82 // nil, 83 // {TopicD1, TopicD2} 84 // } 85 // In order for a message to pass this filter, it should enumerate at least 4 86 // topics, the first any of [TopicA1, TopicA2, TopicA3], the second mandatory 87 // "TopicB", the third is ignored by the filter and the fourth either "TopicD1" 88 // or "TopicD2". If the message contains further topics, the filter will match 89 // them too. 90 type topicMatcher struct { 91 conditions []map[Topic]struct{} 92 } 93 94 // newTopicMatcher create a topic matcher from a list of topic conditions. 95 func newTopicMatcher(topics ...[]Topic) *topicMatcher { 96 matcher := make([]map[Topic]struct{}, len(topics)) 97 for i, condition := range topics { 98 matcher[i] = make(map[Topic]struct{}) 99 for _, topic := range condition { 100 matcher[i][topic] = struct{}{} 101 } 102 } 103 return &topicMatcher{conditions: matcher} 104 } 105 106 // newTopicMatcherFromBinary create a topic matcher from a list of binary conditions. 107 func newTopicMatcherFromBinary(data ...[][]byte) *topicMatcher { 108 topics := make([][]Topic, len(data)) 109 for i, condition := range data { 110 topics[i] = NewTopics(condition...) 111 } 112 return newTopicMatcher(topics...) 113 } 114 115 // newTopicMatcherFromStrings creates a topic matcher from a list of textual 116 // conditions. 117 func newTopicMatcherFromStrings(data ...[]string) *topicMatcher { 118 topics := make([][]Topic, len(data)) 119 for i, condition := range data { 120 topics[i] = NewTopicsFromStrings(condition...) 121 } 122 return newTopicMatcher(topics...) 123 } 124 125 // Matches checks if a list of topics matches this particular condition set. 126 func (self *topicMatcher) Matches(topics []Topic) bool { 127 // Mismatch if there aren't enough topics 128 if len(self.conditions) > len(topics) { 129 return false 130 } 131 // Check each topic condition for existence (skip wild-cards) 132 for i := 0; i < len(topics) && i < len(self.conditions); i++ { 133 if len(self.conditions[i]) > 0 { 134 if _, ok := self.conditions[i][topics[i]]; !ok { 135 return false 136 } 137 } 138 } 139 return true 140 }