1
+ import { sha256 } from "@noble/hashes/sha256" ;
1
2
import type { PubsubTopic , ShardInfo } from "@waku/interfaces" ;
2
3
4
+ import { concat , utf8ToBytes } from "../bytes/index.js" ;
5
+
3
6
export const shardInfoToPubsubTopics = (
4
7
shardInfo : ShardInfo
5
8
) : PubsubTopic [ ] => {
@@ -19,18 +22,28 @@ export function ensurePubsubTopicIsConfigured(
19
22
}
20
23
}
21
24
25
+ interface ContentTopic {
26
+ generation : number ;
27
+ application : string ;
28
+ version : string ;
29
+ topicName : string ;
30
+ encoding : string ;
31
+ }
32
+
22
33
/**
23
34
* Given a string, will throw an error if it is not formatted as a valid content topic for autosharding based on https://rfc.vac.dev/spec/51/
24
35
* @param contentTopic String to validate
36
+ * @returns Object with each content topic field as an attribute
25
37
*/
26
- export function ensureValidContentTopic ( contentTopic : string ) : void {
38
+ export function ensureValidContentTopic ( contentTopic : string ) : ContentTopic {
27
39
const parts = contentTopic . split ( "/" ) ;
28
40
if ( parts . length < 5 || parts . length > 6 ) {
29
41
throw Error ( "Content topic format is invalid" ) ;
30
42
}
31
43
// Validate generation field if present
44
+ let generation = 0 ;
32
45
if ( parts . length == 6 ) {
33
- const generation = parseInt ( parts [ 1 ] ) ;
46
+ generation = parseInt ( parts [ 1 ] ) ;
34
47
if ( isNaN ( generation ) ) {
35
48
throw new Error ( "Invalid generation field in content topic" ) ;
36
49
}
@@ -56,4 +69,37 @@ export function ensureValidContentTopic(contentTopic: string): void {
56
69
if ( fields [ 3 ] . length == 0 ) {
57
70
throw new Error ( "Encoding field cannot be empty" ) ;
58
71
}
72
+
73
+ return {
74
+ generation,
75
+ application : fields [ 0 ] ,
76
+ version : fields [ 1 ] ,
77
+ topicName : fields [ 2 ] ,
78
+ encoding : fields [ 3 ]
79
+ } ;
80
+ }
81
+
82
+ /**
83
+ * Given a string, determines which autoshard index to use for its pubsub topic.
84
+ * Based on the algorithm described in the RFC: https://rfc.vac.dev/spec/51//#algorithm
85
+ */
86
+ export function contentTopicToShardIndex (
87
+ contentTopic : string ,
88
+ networkShards : number = 8
89
+ ) : number {
90
+ const { application, version } = ensureValidContentTopic ( contentTopic ) ;
91
+ const digest = sha256 (
92
+ concat ( [ utf8ToBytes ( application ) , utf8ToBytes ( version ) ] )
93
+ ) ;
94
+ const dataview = new DataView ( digest . buffer . slice ( - 8 ) ) ;
95
+ return Number ( dataview . getBigUint64 ( 0 , false ) % BigInt ( networkShards ) ) ;
96
+ }
97
+
98
+ export function contentTopicToPubsubTopic (
99
+ contentTopic : string ,
100
+ clusterId : number = 1 ,
101
+ networkShards : number = 8
102
+ ) : string {
103
+ const shardIndex = contentTopicToShardIndex ( contentTopic , networkShards ) ;
104
+ return `/waku/2/rs/${ clusterId } /${ shardIndex } ` ;
59
105
}
0 commit comments