topic.go 4.9 KB

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