Skip to content

Commit 2e561fe

Browse files
authored
fix: add pubsub interfaces to @libp2p/interface (#1857)
Adds interfaces from `@libp2p/interface-pubsub` to `/pubsub` export
1 parent 879f479 commit 2e561fe

File tree

2 files changed

+273
-0
lines changed

2 files changed

+273
-0
lines changed

packages/interface/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@
112112
"types": "./dist/src/peer-store/tags.d.ts",
113113
"import": "./dist/src/peer-store/tags.js"
114114
},
115+
"./pubsub": {
116+
"types": "./dist/src/pubsub/index.d.ts",
117+
"import": "./dist/src/pubsub/index.js"
118+
},
115119
"./record": {
116120
"types": "./dist/src/record/index.d.ts",
117121
"import": "./dist/src/record/index.js"
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
import type { Stream } from '../connection/index.js'
2+
import type { EventEmitter } from '../events.js'
3+
import type { PeerId } from '../peer-id/index.js'
4+
import type { Pushable } from 'it-pushable'
5+
import type { Uint8ArrayList } from 'uint8arraylist'
6+
7+
/**
8+
* On the producing side:
9+
* * Build messages with the signature, key (from may be enough for certain inlineable public key types), from and seqno fields.
10+
*
11+
* On the consuming side:
12+
* * Enforce the fields to be present, reject otherwise.
13+
* * Propagate only if the fields are valid and signature can be verified, reject otherwise.
14+
*/
15+
export const StrictSign = 'StrictSign'
16+
17+
/**
18+
* On the producing side:
19+
* * Build messages without the signature, key, from and seqno fields.
20+
* * The corresponding protobuf key-value pairs are absent from the marshalled message, not just empty.
21+
*
22+
* On the consuming side:
23+
* * Enforce the fields to be absent, reject otherwise.
24+
* * Propagate only if the fields are absent, reject otherwise.
25+
* * A message_id function will not be able to use the above fields, and should instead rely on the data field. A commonplace strategy is to calculate a hash.
26+
*/
27+
export const StrictNoSign = 'StrictNoSign'
28+
29+
export type SignaturePolicy = typeof StrictSign | typeof StrictNoSign
30+
31+
export interface SignedMessage {
32+
type: 'signed'
33+
from: PeerId
34+
topic: string
35+
data: Uint8Array
36+
sequenceNumber: bigint
37+
signature: Uint8Array
38+
key: Uint8Array
39+
}
40+
41+
export interface UnsignedMessage {
42+
type: 'unsigned'
43+
topic: string
44+
data: Uint8Array
45+
}
46+
47+
export type Message = SignedMessage | UnsignedMessage
48+
49+
export interface PubSubRPCMessage {
50+
from?: Uint8Array
51+
topic?: string
52+
data?: Uint8Array
53+
sequenceNumber?: Uint8Array
54+
signature?: Uint8Array
55+
key?: Uint8Array
56+
}
57+
58+
export interface PubSubRPCSubscription {
59+
subscribe?: boolean
60+
topic?: string
61+
}
62+
63+
export interface PubSubRPC {
64+
subscriptions: PubSubRPCSubscription[]
65+
messages: PubSubRPCMessage[]
66+
}
67+
68+
export interface PeerStreams extends EventEmitter<PeerStreamEvents> {
69+
id: PeerId
70+
protocol: string
71+
outboundStream?: Pushable<Uint8ArrayList>
72+
inboundStream?: AsyncIterable<Uint8ArrayList>
73+
isWritable: boolean
74+
75+
close: () => void
76+
write: (buf: Uint8Array | Uint8ArrayList) => void
77+
attachInboundStream: (stream: Stream) => AsyncIterable<Uint8ArrayList>
78+
attachOutboundStream: (stream: Stream) => Promise<Pushable<Uint8ArrayList>>
79+
}
80+
81+
export interface PubSubInit {
82+
enabled?: boolean
83+
84+
multicodecs?: string[]
85+
86+
/**
87+
* defines how signatures should be handled
88+
*/
89+
globalSignaturePolicy?: SignaturePolicy
90+
91+
/**
92+
* if can relay messages not subscribed
93+
*/
94+
canRelayMessage?: boolean
95+
96+
/**
97+
* if publish should emit to self, if subscribed
98+
*/
99+
emitSelf?: boolean
100+
101+
/**
102+
* handle this many incoming pubsub messages concurrently
103+
*/
104+
messageProcessingConcurrency?: number
105+
106+
/**
107+
* How many parallel incoming streams to allow on the pubsub protocol per-connection
108+
*/
109+
maxInboundStreams?: number
110+
111+
/**
112+
* How many parallel outgoing streams to allow on the pubsub protocol per-connection
113+
*/
114+
maxOutboundStreams?: number
115+
}
116+
117+
interface Subscription {
118+
topic: string
119+
subscribe: boolean
120+
}
121+
122+
export interface SubscriptionChangeData {
123+
peerId: PeerId
124+
subscriptions: Subscription[]
125+
}
126+
127+
export interface PubSubEvents {
128+
'subscription-change': CustomEvent<SubscriptionChangeData>
129+
'message': CustomEvent<Message>
130+
}
131+
132+
export interface PublishResult {
133+
recipients: PeerId[]
134+
}
135+
136+
export enum TopicValidatorResult {
137+
/**
138+
* The message is considered valid, and it should be delivered and forwarded to the network
139+
*/
140+
Accept = 'accept',
141+
/**
142+
* The message is neither delivered nor forwarded to the network
143+
*/
144+
Ignore = 'ignore',
145+
/**
146+
* The message is considered invalid, and it should be rejected
147+
*/
148+
Reject = 'reject'
149+
}
150+
151+
export interface TopicValidatorFn {
152+
(peer: PeerId, message: Message): TopicValidatorResult | Promise<TopicValidatorResult>
153+
}
154+
155+
export interface PubSub<Events extends Record<string, any> = PubSubEvents> extends EventEmitter<Events> {
156+
/**
157+
* The global signature policy controls whether or not we sill send and receive
158+
* signed or unsigned messages.
159+
*
160+
* Signed messages prevent spoofing message senders and should be preferred to
161+
* using unsigned messages.
162+
*/
163+
globalSignaturePolicy: typeof StrictSign | typeof StrictNoSign
164+
165+
/**
166+
* A list of multicodecs that contain the pubsub protocol name.
167+
*/
168+
multicodecs: string[]
169+
170+
/**
171+
* Pubsub routers support message validators per topic, which will validate the message
172+
* before its propagations. They are stored in a map where keys are the topic name and
173+
* values are the validators.
174+
*
175+
* @example
176+
*
177+
* ```js
178+
* const topic = 'topic'
179+
* const validateMessage = (msgTopic, msg) => {
180+
* const input = uint8ArrayToString(msg.data)
181+
* const validInputs = ['a', 'b', 'c']
182+
*
183+
* if (!validInputs.includes(input)) {
184+
* throw new Error('no valid input received')
185+
* }
186+
* }
187+
* libp2p.pubsub.topicValidators.set(topic, validateMessage)
188+
* ```
189+
*/
190+
topicValidators: Map<string, TopicValidatorFn>
191+
192+
getPeers: () => PeerId[]
193+
194+
/**
195+
* Gets a list of topics the node is subscribed to.
196+
*
197+
* ```js
198+
* const topics = libp2p.pubsub.getTopics()
199+
* ```
200+
*/
201+
getTopics: () => string[]
202+
203+
/**
204+
* Subscribes to a pubsub topic.
205+
*
206+
* @example
207+
*
208+
* ```js
209+
* const topic = 'topic'
210+
* const handler = (msg) => {
211+
* if (msg.topic === topic) {
212+
* // msg.data - pubsub data received
213+
* }
214+
* }
215+
*
216+
* libp2p.pubsub.addEventListener('message', handler)
217+
* libp2p.pubsub.subscribe(topic)
218+
* ```
219+
*/
220+
subscribe: (topic: string) => void
221+
222+
/**
223+
* Unsubscribes from a pubsub topic.
224+
*
225+
* @example
226+
*
227+
* ```js
228+
* const topic = 'topic'
229+
* const handler = (msg) => {
230+
* // msg.data - pubsub data received
231+
* }
232+
*
233+
* libp2p.pubsub.removeEventListener(topic handler)
234+
* libp2p.pubsub.unsubscribe(topic)
235+
* ```
236+
*/
237+
unsubscribe: (topic: string) => void
238+
239+
/**
240+
* Gets a list of the PeerIds that are subscribed to one topic.
241+
*
242+
* @example
243+
*
244+
* ```js
245+
* const peerIds = libp2p.pubsub.getSubscribers(topic)
246+
* ```
247+
*/
248+
getSubscribers: (topic: string) => PeerId[]
249+
250+
/**
251+
* Publishes messages to the given topic.
252+
*
253+
* @example
254+
*
255+
* ```js
256+
* const topic = 'topic'
257+
* const data = uint8ArrayFromString('data')
258+
*
259+
* await libp2p.pubsub.publish(topic, data)
260+
* ```
261+
*/
262+
publish: (topic: string, data: Uint8Array) => Promise<PublishResult>
263+
}
264+
265+
export interface PeerStreamEvents {
266+
'stream:inbound': CustomEvent<never>
267+
'stream:outbound': CustomEvent<never>
268+
'close': CustomEvent<never>
269+
}

0 commit comments

Comments
 (0)