mirror of https://github.com/status-im/go-waku.git
parent
cb3f5da322
commit
44d3ef6d78
|
@ -199,8 +199,8 @@ func waku_peer_cnt(onOkCb C.WakuCallBack, onErrCb C.WakuCallBack) C.int {
|
||||||
//
|
//
|
||||||
//export waku_content_topic
|
//export waku_content_topic
|
||||||
func waku_content_topic(applicationName *C.char, applicationVersion C.uint, contentTopicName *C.char, encoding *C.char, onOkCb C.WakuCallBack) C.int {
|
func waku_content_topic(applicationName *C.char, applicationVersion C.uint, contentTopicName *C.char, encoding *C.char, onOkCb C.WakuCallBack) C.int {
|
||||||
contentTopic := protocol.NewContentTopic(C.GoString(applicationName), uint(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding)).String()
|
contentTopic, _ := protocol.NewContentTopic(C.GoString(applicationName), uint32(applicationVersion), C.GoString(contentTopicName), C.GoString(encoding))
|
||||||
return execOkCB(onOkCb, contentTopic)
|
return execOkCB(onOkCb, contentTopic.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a pubsub topic string according to RFC 23
|
// Create a pubsub topic string according to RFC 23
|
||||||
|
|
|
@ -74,7 +74,8 @@ func PeerCnt() string {
|
||||||
|
|
||||||
// ContentTopic creates a content topic string according to RFC 23
|
// ContentTopic creates a content topic string according to RFC 23
|
||||||
func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string {
|
func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string {
|
||||||
return protocol.NewContentTopic(applicationName, uint(applicationVersion), contentTopicName, encoding).String()
|
contentTopic, _ := protocol.NewContentTopic(applicationName, uint32(applicationVersion), contentTopicName, encoding)
|
||||||
|
return contentTopic.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubsubTopic creates a pubsub topic string according to RFC 23
|
// PubsubTopic creates a pubsub topic string according to RFC 23
|
||||||
|
|
|
@ -336,7 +336,8 @@ func PeerCnt() (int, error) {
|
||||||
|
|
||||||
// ContentTopic creates a content topic string according to RFC 23
|
// ContentTopic creates a content topic string according to RFC 23
|
||||||
func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string {
|
func ContentTopic(applicationName string, applicationVersion int, contentTopicName string, encoding string) string {
|
||||||
return protocol.NewContentTopic(applicationName, uint(applicationVersion), contentTopicName, encoding).String()
|
contentTopic, _ := protocol.NewContentTopic(applicationName, uint32(applicationVersion), contentTopicName, encoding)
|
||||||
|
return contentTopic.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubsubTopic creates a pubsub topic string according to RFC 23
|
// PubsubTopic creates a pubsub topic string according to RFC 23
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultContentTopic is the default content topic used in Waku network if no content topic is specified.
|
||||||
|
const DefaultContentTopic = "/waku/2/default-content/proto"
|
||||||
|
|
||||||
|
var ErrInvalidFormat = errors.New("invalid format")
|
||||||
|
var ErrMissingGeneration = errors.New("missing part: generation")
|
||||||
|
var ErrInvalidGeneration = errors.New("generation should be a number")
|
||||||
|
|
||||||
|
// ContentTopic is used for content based.
|
||||||
|
type ContentTopic struct {
|
||||||
|
ContentTopicParams
|
||||||
|
ApplicationName string
|
||||||
|
ApplicationVersion uint32
|
||||||
|
ContentTopicName string
|
||||||
|
Encoding string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentTopicParams contains all the optional params for a content topic
|
||||||
|
type ContentTopicParams struct {
|
||||||
|
Generation int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal method used to compare 2 contentTopicParams
|
||||||
|
func (ctp ContentTopicParams) Equal(ctp2 ContentTopicParams) bool {
|
||||||
|
return ctp.Generation == ctp2.Generation
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentTopicOption is following the options pattern to define optional params
|
||||||
|
type ContentTopicOption func(*ContentTopicParams)
|
||||||
|
|
||||||
|
// String formats a content topic in string format as per RFC 23.
|
||||||
|
func (ct ContentTopic) String() string {
|
||||||
|
return fmt.Sprintf("/%s/%d/%s/%s", ct.ApplicationName, ct.ApplicationVersion, ct.ContentTopicName, ct.Encoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContentTopic creates a new content topic based on params specified.
|
||||||
|
// Returns ErrInvalidGeneration if an unsupported generation is specified.
|
||||||
|
func NewContentTopic(applicationName string, applicationVersion uint32,
|
||||||
|
contentTopicName string, encoding string, opts ...ContentTopicOption) (ContentTopic, error) {
|
||||||
|
|
||||||
|
params := new(ContentTopicParams)
|
||||||
|
optList := DefaultOptions()
|
||||||
|
optList = append(optList, opts...)
|
||||||
|
for _, opt := range optList {
|
||||||
|
opt(params)
|
||||||
|
}
|
||||||
|
if params.Generation > 0 {
|
||||||
|
return ContentTopic{}, ErrInvalidGeneration
|
||||||
|
}
|
||||||
|
return ContentTopic{
|
||||||
|
ContentTopicParams: *params,
|
||||||
|
ApplicationName: applicationName,
|
||||||
|
ApplicationVersion: applicationVersion,
|
||||||
|
ContentTopicName: contentTopicName,
|
||||||
|
Encoding: encoding,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGeneration option can be used to specify explicitly a generation for contentTopic
|
||||||
|
func WithGeneration(generation int) ContentTopicOption {
|
||||||
|
return func(params *ContentTopicParams) {
|
||||||
|
params.Generation = generation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultOptions sets default values for contentTopic optional params.
|
||||||
|
func DefaultOptions() []ContentTopicOption {
|
||||||
|
return []ContentTopicOption{
|
||||||
|
WithGeneration(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal to compare 2 content topics.
|
||||||
|
func (ct ContentTopic) Equal(ct2 ContentTopic) bool {
|
||||||
|
return ct.ApplicationName == ct2.ApplicationName && ct.ApplicationVersion == ct2.ApplicationVersion &&
|
||||||
|
ct.ContentTopicName == ct2.ContentTopicName && ct.Encoding == ct2.Encoding &&
|
||||||
|
ct.ContentTopicParams.Equal(ct2.ContentTopicParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToContentTopic can be used to create a ContentTopic object from a string
|
||||||
|
func StringToContentTopic(s string) (ContentTopic, error) {
|
||||||
|
p := strings.Split(s, "/")
|
||||||
|
switch len(p) {
|
||||||
|
case 5:
|
||||||
|
vNum, err := strconv.ParseUint(p[2], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return ContentTopic{}, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContentTopic{
|
||||||
|
ApplicationName: p[1],
|
||||||
|
ApplicationVersion: uint32(vNum),
|
||||||
|
ContentTopicName: p[3],
|
||||||
|
Encoding: p[4],
|
||||||
|
}, nil
|
||||||
|
case 6:
|
||||||
|
if len(p[1]) == 0 {
|
||||||
|
return ContentTopic{}, ErrMissingGeneration
|
||||||
|
}
|
||||||
|
generation, err := strconv.Atoi(p[1])
|
||||||
|
if err != nil || generation > 0 {
|
||||||
|
return ContentTopic{}, ErrInvalidGeneration
|
||||||
|
}
|
||||||
|
vNum, err := strconv.ParseUint(p[3], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return ContentTopic{}, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContentTopic{
|
||||||
|
ContentTopicParams: ContentTopicParams{Generation: generation},
|
||||||
|
ApplicationName: p[2],
|
||||||
|
ApplicationVersion: uint32(vNum),
|
||||||
|
ContentTopicName: p[4],
|
||||||
|
Encoding: p[5],
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return ContentTopic{}, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import (
|
||||||
const Waku2PubsubTopicPrefix = "/waku/2"
|
const Waku2PubsubTopicPrefix = "/waku/2"
|
||||||
const StaticShardingPubsubTopicPrefix = Waku2PubsubTopicPrefix + "/rs"
|
const StaticShardingPubsubTopicPrefix = Waku2PubsubTopicPrefix + "/rs"
|
||||||
|
|
||||||
var ErrInvalidFormat = errors.New("invalid format")
|
|
||||||
var ErrInvalidStructure = errors.New("invalid topic structure")
|
var ErrInvalidStructure = errors.New("invalid topic structure")
|
||||||
var ErrInvalidTopicPrefix = errors.New("must start with " + Waku2PubsubTopicPrefix)
|
var ErrInvalidTopicPrefix = errors.New("must start with " + Waku2PubsubTopicPrefix)
|
||||||
var ErrMissingTopicName = errors.New("missing topic-name")
|
var ErrMissingTopicName = errors.New("missing topic-name")
|
||||||
|
@ -19,51 +18,7 @@ var ErrMissingClusterIndex = errors.New("missing shard_cluster_index")
|
||||||
var ErrMissingShardNumber = errors.New("missing shard_number")
|
var ErrMissingShardNumber = errors.New("missing shard_number")
|
||||||
var ErrInvalidNumberFormat = errors.New("only 2^16 numbers are allowed")
|
var ErrInvalidNumberFormat = errors.New("only 2^16 numbers are allowed")
|
||||||
|
|
||||||
type ContentTopic struct {
|
// NamespacedPubsubTopicKind used to represent kind of NamespacedPubsubTopicKind
|
||||||
ApplicationName string
|
|
||||||
ApplicationVersion uint
|
|
||||||
ContentTopicName string
|
|
||||||
Encoding string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct ContentTopic) String() string {
|
|
||||||
return fmt.Sprintf("/%s/%d/%s/%s", ct.ApplicationName, ct.ApplicationVersion, ct.ContentTopicName, ct.Encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewContentTopic(applicationName string, applicationVersion uint, contentTopicName string, encoding string) ContentTopic {
|
|
||||||
return ContentTopic{
|
|
||||||
ApplicationName: applicationName,
|
|
||||||
ApplicationVersion: applicationVersion,
|
|
||||||
ContentTopicName: contentTopicName,
|
|
||||||
Encoding: encoding,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct ContentTopic) Equal(ct2 ContentTopic) bool {
|
|
||||||
return ct.ApplicationName == ct2.ApplicationName && ct.ApplicationVersion == ct2.ApplicationVersion &&
|
|
||||||
ct.ContentTopicName == ct2.ContentTopicName && ct.Encoding == ct2.Encoding
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringToContentTopic(s string) (ContentTopic, error) {
|
|
||||||
p := strings.Split(s, "/")
|
|
||||||
|
|
||||||
if len(p) != 5 || p[0] != "" || p[1] == "" || p[2] == "" || p[3] == "" || p[4] == "" {
|
|
||||||
return ContentTopic{}, ErrInvalidFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
vNum, err := strconv.ParseUint(p[2], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return ContentTopic{}, ErrInvalidFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
return ContentTopic{
|
|
||||||
ApplicationName: p[1],
|
|
||||||
ApplicationVersion: uint(vNum),
|
|
||||||
ContentTopicName: p[3],
|
|
||||||
Encoding: p[4],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type NamespacedPubsubTopicKind int
|
type NamespacedPubsubTopicKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -71,18 +26,21 @@ const (
|
||||||
NamedSharding
|
NamedSharding
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NamespacedPubsubTopic is an interface for namespace based pubSub topic
|
||||||
type NamespacedPubsubTopic interface {
|
type NamespacedPubsubTopic interface {
|
||||||
String() string
|
String() string
|
||||||
Kind() NamespacedPubsubTopicKind
|
Kind() NamespacedPubsubTopicKind
|
||||||
Equal(NamespacedPubsubTopic) bool
|
Equal(NamespacedPubsubTopic) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamedShardingPubsubTopic is object for a NamedSharding type pubSub topic
|
||||||
type NamedShardingPubsubTopic struct {
|
type NamedShardingPubsubTopic struct {
|
||||||
NamespacedPubsubTopic
|
NamespacedPubsubTopic
|
||||||
kind NamespacedPubsubTopicKind
|
kind NamespacedPubsubTopicKind
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewNamedShardingPubsubTopic creates a new NamedShardingPubSubTopic
|
||||||
func NewNamedShardingPubsubTopic(name string) NamespacedPubsubTopic {
|
func NewNamedShardingPubsubTopic(name string) NamespacedPubsubTopic {
|
||||||
return NamedShardingPubsubTopic{
|
return NamedShardingPubsubTopic{
|
||||||
kind: NamedSharding,
|
kind: NamedSharding,
|
||||||
|
@ -90,23 +48,28 @@ func NewNamedShardingPubsubTopic(name string) NamespacedPubsubTopic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kind returns the type of PubsubTopic whether it is StaticShared or NamedSharded
|
||||||
func (n NamedShardingPubsubTopic) Kind() NamespacedPubsubTopicKind {
|
func (n NamedShardingPubsubTopic) Kind() NamespacedPubsubTopicKind {
|
||||||
return n.kind
|
return n.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name is the name of the NamedSharded pubsub topic.
|
||||||
func (n NamedShardingPubsubTopic) Name() string {
|
func (n NamedShardingPubsubTopic) Name() string {
|
||||||
return n.name
|
return n.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s NamedShardingPubsubTopic) Equal(t2 NamespacedPubsubTopic) bool {
|
// Equal compares NamedShardingPubsubTopic
|
||||||
return s.String() == t2.String()
|
func (n NamedShardingPubsubTopic) Equal(t2 NamespacedPubsubTopic) bool {
|
||||||
|
return n.String() == t2.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String formats NamedShardingPubsubTopic to RFC 23 specific string format for pubsub topic.
|
||||||
func (n NamedShardingPubsubTopic) String() string {
|
func (n NamedShardingPubsubTopic) String() string {
|
||||||
return fmt.Sprintf("%s/%s", Waku2PubsubTopicPrefix, n.name)
|
return fmt.Sprintf("%s/%s", Waku2PubsubTopicPrefix, n.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NamedShardingPubsubTopic) Parse(topic string) error {
|
// Parse parses a topic string into a NamedShardingPubsubTopic
|
||||||
|
func (n *NamedShardingPubsubTopic) Parse(topic string) error {
|
||||||
if !strings.HasPrefix(topic, Waku2PubsubTopicPrefix) {
|
if !strings.HasPrefix(topic, Waku2PubsubTopicPrefix) {
|
||||||
return ErrInvalidTopicPrefix
|
return ErrInvalidTopicPrefix
|
||||||
}
|
}
|
||||||
|
@ -116,12 +79,13 @@ func (s *NamedShardingPubsubTopic) Parse(topic string) error {
|
||||||
return ErrMissingTopicName
|
return ErrMissingTopicName
|
||||||
}
|
}
|
||||||
|
|
||||||
s.kind = NamedSharding
|
n.kind = NamedSharding
|
||||||
s.name = topicName
|
n.name = topicName
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StaticShardingPubsubTopic describes a pubSub topic as per StaticSharding
|
||||||
type StaticShardingPubsubTopic struct {
|
type StaticShardingPubsubTopic struct {
|
||||||
NamespacedPubsubTopic
|
NamespacedPubsubTopic
|
||||||
kind NamespacedPubsubTopicKind
|
kind NamespacedPubsubTopicKind
|
||||||
|
@ -129,6 +93,7 @@ type StaticShardingPubsubTopic struct {
|
||||||
shard uint16
|
shard uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewStaticShardingPubsubTopic creates a new pubSub topic
|
||||||
func NewStaticShardingPubsubTopic(cluster uint16, shard uint16) NamespacedPubsubTopic {
|
func NewStaticShardingPubsubTopic(cluster uint16, shard uint16) NamespacedPubsubTopic {
|
||||||
return StaticShardingPubsubTopic{
|
return StaticShardingPubsubTopic{
|
||||||
kind: StaticSharding,
|
kind: StaticSharding,
|
||||||
|
@ -137,26 +102,32 @@ func NewStaticShardingPubsubTopic(cluster uint16, shard uint16) NamespacedPubsub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StaticShardingPubsubTopic) Cluster() uint16 {
|
// Cluster returns the sharded cluster index
|
||||||
return n.cluster
|
func (s StaticShardingPubsubTopic) Cluster() uint16 {
|
||||||
|
return s.cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StaticShardingPubsubTopic) Shard() uint16 {
|
// Cluster returns the shard number
|
||||||
return n.shard
|
func (s StaticShardingPubsubTopic) Shard() uint16 {
|
||||||
|
return s.shard
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StaticShardingPubsubTopic) Kind() NamespacedPubsubTopicKind {
|
// Kind returns the type of PubsubTopic whether it is StaticShared or NamedSharded
|
||||||
return n.kind
|
func (s StaticShardingPubsubTopic) Kind() NamespacedPubsubTopicKind {
|
||||||
|
return s.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equal compares StaticShardingPubsubTopic
|
||||||
func (s StaticShardingPubsubTopic) Equal(t2 NamespacedPubsubTopic) bool {
|
func (s StaticShardingPubsubTopic) Equal(t2 NamespacedPubsubTopic) bool {
|
||||||
return s.String() == t2.String()
|
return s.String() == t2.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StaticShardingPubsubTopic) String() string {
|
// String formats StaticShardingPubsubTopic to RFC 23 specific string format for pubsub topic.
|
||||||
return fmt.Sprintf("%s/%d/%d", StaticShardingPubsubTopicPrefix, n.cluster, n.shard)
|
func (s StaticShardingPubsubTopic) String() string {
|
||||||
|
return fmt.Sprintf("%s/%d/%d", StaticShardingPubsubTopicPrefix, s.cluster, s.shard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse parses a topic string into a StaticShardingPubsubTopic
|
||||||
func (s *StaticShardingPubsubTopic) Parse(topic string) error {
|
func (s *StaticShardingPubsubTopic) Parse(topic string) error {
|
||||||
if !strings.HasPrefix(topic, StaticShardingPubsubTopicPrefix) {
|
if !strings.HasPrefix(topic, StaticShardingPubsubTopicPrefix) {
|
||||||
return ErrInvalidShardedTopicPrefix
|
return ErrInvalidShardedTopicPrefix
|
||||||
|
@ -194,6 +165,7 @@ func (s *StaticShardingPubsubTopic) Parse(topic string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToShardedPubsubTopic takes a pubSub topic string and creates a NamespacedPubsubTopic object.
|
||||||
func ToShardedPubsubTopic(topic string) (NamespacedPubsubTopic, error) {
|
func ToShardedPubsubTopic(topic string) (NamespacedPubsubTopic, error) {
|
||||||
if strings.HasPrefix(topic, StaticShardingPubsubTopicPrefix) {
|
if strings.HasPrefix(topic, StaticShardingPubsubTopicPrefix) {
|
||||||
s := StaticShardingPubsubTopic{}
|
s := StaticShardingPubsubTopic{}
|
||||||
|
@ -212,6 +184,7 @@ func ToShardedPubsubTopic(topic string) (NamespacedPubsubTopic, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultPubsubTopic is the default pubSub topic used in waku
|
||||||
func DefaultPubsubTopic() NamespacedPubsubTopic {
|
func DefaultPubsubTopic() NamespacedPubsubTopic {
|
||||||
return NewNamedShardingPubsubTopic("default-waku/proto")
|
return NewNamedShardingPubsubTopic("default-waku/proto")
|
||||||
}
|
}
|
|
@ -6,10 +6,19 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxShardIndex = uint16(1023)
|
const MaxShardIndex = uint16(1023)
|
||||||
|
|
||||||
|
// ClusterIndex is the clusterID used in sharding space.
|
||||||
|
// For indices allocation and other magic numbers refer to RFC 51
|
||||||
|
const ClusterIndex = 1
|
||||||
|
|
||||||
|
// GenerationZeroShardsCount is number of shards supported in generation-0
|
||||||
|
const GenerationZeroShardsCount = 8
|
||||||
|
|
||||||
type RelayShards struct {
|
type RelayShards struct {
|
||||||
Cluster uint16
|
Cluster uint16
|
||||||
Indices []uint16
|
Indices []uint16
|
||||||
|
@ -205,3 +214,18 @@ func FromBitVector(buf []byte) (RelayShards, error) {
|
||||||
|
|
||||||
return RelayShards{Cluster: cluster, Indices: indices}, nil
|
return RelayShards{Cluster: cluster, Indices: indices}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetShardFromContentTopic runs Autosharding logic and returns a pubSubTopic
|
||||||
|
// This is based on Autosharding algorithm defined in RFC 51
|
||||||
|
func GetShardFromContentTopic(topic ContentTopic, shardCount int) NamespacedPubsubTopic {
|
||||||
|
bytes := []byte(topic.ApplicationName)
|
||||||
|
bytes = append(bytes, []byte(fmt.Sprintf("%d", topic.ApplicationVersion))...)
|
||||||
|
|
||||||
|
hash := hash.SHA256(bytes)
|
||||||
|
//We only use the last 64 bits of the hash as having more shards is unlikely.
|
||||||
|
hashValue := binary.BigEndian.Uint64(hash[24:])
|
||||||
|
|
||||||
|
shard := hashValue % uint64(shardCount)
|
||||||
|
|
||||||
|
return NewStaticShardingPubsubTopic(ClusterIndex, uint16(shard))
|
||||||
|
}
|
||||||
|
|
|
@ -27,14 +27,14 @@ func TestIndexComputation(t *testing.T) {
|
||||||
msg1 := &wpb.WakuMessage{
|
msg1 := &wpb.WakuMessage{
|
||||||
Payload: []byte{1, 2, 3},
|
Payload: []byte{1, 2, 3},
|
||||||
Timestamp: 123,
|
Timestamp: 123,
|
||||||
ContentTopic: "/waku/2/default-content/proto",
|
ContentTopic: protocol.DefaultContentTopic,
|
||||||
}
|
}
|
||||||
idx1 := protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), "test").Index()
|
idx1 := protocol.NewEnvelope(msg1, utils.GetUnixEpoch(), "test").Index()
|
||||||
|
|
||||||
msg2 := &wpb.WakuMessage{
|
msg2 := &wpb.WakuMessage{
|
||||||
Payload: []byte{1, 2, 3},
|
Payload: []byte{1, 2, 3},
|
||||||
Timestamp: 123,
|
Timestamp: 123,
|
||||||
ContentTopic: "/waku/2/default-content/proto",
|
ContentTopic: protocol.DefaultContentTopic,
|
||||||
}
|
}
|
||||||
idx2 := protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), "test").Index()
|
idx2 := protocol.NewEnvelope(msg2, utils.GetUnixEpoch(), "test").Index()
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,12 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContentTopic(t *testing.T) {
|
func TestContentTopicAndSharding(t *testing.T) {
|
||||||
ct := NewContentTopic("waku", 2, "test", "proto")
|
ct, err := NewContentTopic("waku", 2, "test", "proto")
|
||||||
|
require.NoError(t, err)
|
||||||
require.Equal(t, ct.String(), "/waku/2/test/proto")
|
require.Equal(t, ct.String(), "/waku/2/test/proto")
|
||||||
|
|
||||||
_, err := StringToContentTopic("/waku/-1/a/b")
|
_, err = StringToContentTopic("/waku/-1/a/b")
|
||||||
require.Error(t, ErrInvalidFormat, err)
|
require.Error(t, ErrInvalidFormat, err)
|
||||||
|
|
||||||
_, err = StringToContentTopic("waku/1/a/b")
|
_, err = StringToContentTopic("waku/1/a/b")
|
||||||
|
@ -27,8 +28,29 @@ func TestContentTopic(t *testing.T) {
|
||||||
require.Equal(t, ct.String(), ct2.String())
|
require.Equal(t, ct.String(), ct2.String())
|
||||||
require.True(t, ct.Equal(ct2))
|
require.True(t, ct.Equal(ct2))
|
||||||
|
|
||||||
ct3 := NewContentTopic("waku", 2, "test2", "proto")
|
ct3, err := NewContentTopic("waku", 2, "test2", "proto")
|
||||||
|
require.NoError(t, err)
|
||||||
require.False(t, ct.Equal(ct3))
|
require.False(t, ct.Equal(ct3))
|
||||||
|
|
||||||
|
ct4, err := StringToContentTopic("/0/toychat/2/huilong/proto")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, ct4.Generation, 0)
|
||||||
|
|
||||||
|
ct6, err := StringToContentTopic("/toychat/2/huilong/proto")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
nsPubSubT1 := GetShardFromContentTopic(ct6, GenerationZeroShardsCount)
|
||||||
|
require.Equal(t, NewStaticShardingPubsubTopic(ClusterIndex, 3), nsPubSubT1)
|
||||||
|
|
||||||
|
_, err = StringToContentTopic("/abc/toychat/2/huilong/proto")
|
||||||
|
require.Error(t, ErrInvalidGeneration, err)
|
||||||
|
|
||||||
|
_, err = StringToContentTopic("/1/toychat/2/huilong/proto")
|
||||||
|
require.Error(t, ErrInvalidGeneration, err)
|
||||||
|
|
||||||
|
ct5, err := NewContentTopic("waku", 2, "test2", "proto", WithGeneration(0))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, ct5.Generation, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNsPubsubTopic(t *testing.T) {
|
func TestNsPubsubTopic(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue