@libp2p/floodsub 10.1.46-6059227cb → 10.1.46-8484de8a2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,200 +0,0 @@
1
- import { AbortError } from '@libp2p/interface'
2
- import { pipe } from '@libp2p/utils'
3
- import * as lp from 'it-length-prefixed'
4
- import { pushable } from 'it-pushable'
5
- import { TypedEventEmitter } from 'main-event'
6
- import { pEvent } from 'p-event'
7
- import { Uint8ArrayList } from 'uint8arraylist'
8
- import type { PeerStreamEvents } from './index.ts'
9
- import type { ComponentLogger, Logger, Stream, PeerId } from '@libp2p/interface'
10
- import type { DecoderOptions as LpDecoderOptions } from 'it-length-prefixed'
11
- import type { Pushable } from 'it-pushable'
12
-
13
- export interface PeerStreamsInit {
14
- id: PeerId
15
- protocol: string
16
- }
17
-
18
- export interface PeerStreamsComponents {
19
- logger: ComponentLogger
20
- }
21
-
22
- export interface DecoderOptions extends LpDecoderOptions {
23
- // other custom options we might want for `attachInboundStream`
24
- }
25
-
26
- /**
27
- * Thin wrapper around a peer's inbound / outbound pubsub streams
28
- */
29
- export class PeerStreams extends TypedEventEmitter<PeerStreamEvents> {
30
- public readonly id: PeerId
31
- public readonly protocol: string
32
- /**
33
- * Write stream - it's preferable to use the write method
34
- */
35
- public outboundStream?: Pushable<Uint8ArrayList>
36
- /**
37
- * Read stream
38
- */
39
- public inboundStream?: AsyncIterable<Uint8ArrayList>
40
- /**
41
- * The raw outbound stream, as retrieved from conn.newStream
42
- */
43
- private _rawOutboundStream?: Stream
44
- /**
45
- * The raw inbound stream, as retrieved from the callback from libp2p.handle
46
- */
47
- private _rawInboundStream?: Stream
48
- /**
49
- * An AbortController for controlled shutdown of the inbound stream
50
- */
51
- private readonly _inboundAbortController: AbortController
52
- private closed: boolean
53
- private readonly log: Logger
54
-
55
- constructor (components: PeerStreamsComponents, init: PeerStreamsInit) {
56
- super()
57
-
58
- this.log = components.logger.forComponent('libp2p-pubsub:peer-streams')
59
- this.id = init.id
60
- this.protocol = init.protocol
61
-
62
- this._inboundAbortController = new AbortController()
63
- this.closed = false
64
- }
65
-
66
- /**
67
- * Do we have a connection to read from?
68
- */
69
- get isReadable (): boolean {
70
- return Boolean(this.inboundStream)
71
- }
72
-
73
- /**
74
- * Do we have a connection to write on?
75
- */
76
- get isWritable (): boolean {
77
- return Boolean(this.outboundStream)
78
- }
79
-
80
- /**
81
- * Send a message to this peer.
82
- * Throws if there is no `stream` to write to available.
83
- */
84
- write (data: Uint8Array | Uint8ArrayList): void {
85
- if (this.outboundStream == null) {
86
- const id = this.id.toString()
87
- throw new Error('No writable connection to ' + id)
88
- }
89
-
90
- this.outboundStream.push(data instanceof Uint8Array ? new Uint8ArrayList(data) : data)
91
- }
92
-
93
- /**
94
- * Attach a raw inbound stream and setup a read stream
95
- */
96
- attachInboundStream (stream: Stream, decoderOptions?: DecoderOptions): AsyncIterable<Uint8ArrayList> {
97
- const abortListener = (): void => {
98
- stream.abort(new AbortError())
99
- }
100
-
101
- this._inboundAbortController.signal.addEventListener('abort', abortListener, {
102
- once: true
103
- })
104
-
105
- // Create and attach a new inbound stream
106
- // The inbound stream is:
107
- // - abortable, set to only return on abort, rather than throw
108
- // - transformed with length-prefix transform
109
- this._rawInboundStream = stream
110
- this.inboundStream = pipe(
111
- this._rawInboundStream,
112
- (source) => lp.decode(source, decoderOptions)
113
- )
114
-
115
- this.dispatchEvent(new CustomEvent('stream:inbound'))
116
- return this.inboundStream
117
- }
118
-
119
- /**
120
- * Attach a raw outbound stream and setup a write stream
121
- */
122
- async attachOutboundStream (stream: Stream): Promise<Pushable<Uint8ArrayList>> {
123
- // If an outbound stream already exists, gently close it
124
- const _prevStream = this.outboundStream
125
- if (this.outboundStream != null) {
126
- // End the stream without emitting a close event
127
- this.outboundStream.end()
128
- }
129
-
130
- this._rawOutboundStream = stream
131
- this.outboundStream = pushable<Uint8ArrayList>({
132
- onEnd: (shouldEmit) => {
133
- // close writable side of the stream if it exists
134
- this._rawOutboundStream?.close()
135
- .catch(err => {
136
- this.log('error closing outbound stream', err)
137
- })
138
-
139
- this._rawOutboundStream = undefined
140
- this.outboundStream = undefined
141
- if (shouldEmit != null) {
142
- this.dispatchEvent(new CustomEvent('close'))
143
- }
144
- }
145
- })
146
-
147
- pipe(
148
- this.outboundStream,
149
- (source) => lp.encode(source),
150
- async (source) => {
151
- for await (const buf of source) {
152
- const sendMore = stream.send(buf)
153
-
154
- if (sendMore === false) {
155
- await pEvent(stream, 'drain', {
156
- rejectionEvents: [
157
- 'close'
158
- ]
159
- })
160
- }
161
- }
162
- }
163
- ).catch((err: Error) => {
164
- this.log.error(err)
165
- })
166
-
167
- // Only emit if the connection is new
168
- if (_prevStream == null) {
169
- this.dispatchEvent(new CustomEvent('stream:outbound'))
170
- }
171
-
172
- return this.outboundStream
173
- }
174
-
175
- /**
176
- * Closes the open connection to peer
177
- */
178
- close (): void {
179
- if (this.closed) {
180
- return
181
- }
182
-
183
- this.closed = true
184
-
185
- // End the outbound stream
186
- if (this.outboundStream != null) {
187
- this.outboundStream.end()
188
- }
189
- // End the inbound stream
190
- if (this.inboundStream != null) {
191
- this._inboundAbortController.abort()
192
- }
193
-
194
- this._rawOutboundStream = undefined
195
- this.outboundStream = undefined
196
- this._rawInboundStream = undefined
197
- this.inboundStream = undefined
198
- this.dispatchEvent(new CustomEvent('close'))
199
- }
200
- }
package/src/sign.ts DELETED
@@ -1,93 +0,0 @@
1
- import { peerIdFromPrivateKey } from '@libp2p/peer-id'
2
- import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
3
- import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
- import { toRpcMessage } from './utils.js'
5
- import type { PubSubRPCMessage } from './floodsub.ts'
6
- import type { SignedMessage } from './index.ts'
7
- import type { PeerId, PrivateKey, PublicKey } from '@libp2p/interface'
8
-
9
- export const SignPrefix = uint8ArrayFromString('libp2p-pubsub:')
10
-
11
- /**
12
- * Signs the provided message with the given `peerId`
13
- */
14
- export async function signMessage (privateKey: PrivateKey, message: { from: PeerId, topic: string, data: Uint8Array, sequenceNumber: bigint }, encode: (rpc: PubSubRPCMessage) => Uint8Array): Promise<SignedMessage> {
15
- // @ts-expect-error signature field is missing, added below
16
- const outputMessage: SignedMessage = {
17
- type: 'signed',
18
- topic: message.topic,
19
- data: message.data,
20
- sequenceNumber: message.sequenceNumber,
21
- from: peerIdFromPrivateKey(privateKey)
22
- }
23
-
24
- // Get the message in bytes, and prepend with the pubsub prefix
25
- const bytes = uint8ArrayConcat([
26
- SignPrefix,
27
- encode(toRpcMessage(outputMessage)).subarray()
28
- ])
29
-
30
- outputMessage.signature = await privateKey.sign(bytes)
31
- outputMessage.key = privateKey.publicKey
32
-
33
- return outputMessage
34
- }
35
-
36
- /**
37
- * Verifies the signature of the given message
38
- */
39
- export async function verifySignature (message: SignedMessage, encode: (rpc: PubSubRPCMessage) => Uint8Array): Promise<boolean> {
40
- if (message.type !== 'signed') {
41
- throw new Error('Message type must be "signed" to be verified')
42
- }
43
-
44
- if (message.signature == null) {
45
- throw new Error('Message must contain a signature to be verified')
46
- }
47
-
48
- if (message.from == null) {
49
- throw new Error('Message must contain a from property to be verified')
50
- }
51
-
52
- // Get message sans the signature
53
- const bytes = uint8ArrayConcat([
54
- SignPrefix,
55
- encode({
56
- ...toRpcMessage(message),
57
- signature: undefined,
58
- key: undefined
59
- }).subarray()
60
- ])
61
-
62
- // Get the public key
63
- const pubKey = messagePublicKey(message)
64
-
65
- // verify the base message
66
- return pubKey.verify(bytes, message.signature)
67
- }
68
-
69
- /**
70
- * Returns the PublicKey associated with the given message.
71
- * If no valid PublicKey can be retrieved an error will be returned.
72
- */
73
- export function messagePublicKey (message: SignedMessage): PublicKey {
74
- if (message.type !== 'signed') {
75
- throw new Error('Message type must be "signed" to have a public key')
76
- }
77
-
78
- // should be available in the from property of the message (peer id)
79
- if (message.from == null) {
80
- throw new Error('Could not get the public key from the originator id')
81
- }
82
-
83
- if (message.key != null) {
84
- return message.key
85
- }
86
-
87
- if (message.from.publicKey != null) {
88
- return message.from.publicKey
89
- }
90
-
91
- // We couldn't validate pubkey is from the originator, error
92
- throw new Error('Could not get the public key from the originator id')
93
- }
package/src/utils.ts DELETED
@@ -1,157 +0,0 @@
1
- import { randomBytes } from '@libp2p/crypto'
2
- import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys'
3
- import { InvalidMessageError } from '@libp2p/interface'
4
- import { peerIdFromMultihash, peerIdFromPublicKey } from '@libp2p/peer-id'
5
- import * as Digest from 'multiformats/hashes/digest'
6
- import { sha256 } from 'multiformats/hashes/sha2'
7
- import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
8
- import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
9
- import type { PubSubRPCMessage } from './floodsub.ts'
10
- import type { Message } from './index.ts'
11
- import type { PublicKey } from '@libp2p/interface'
12
-
13
- /**
14
- * Generate a random sequence number
15
- */
16
- export function randomSeqno (): bigint {
17
- return BigInt(`0x${uint8ArrayToString(randomBytes(8), 'base16')}`)
18
- }
19
-
20
- /**
21
- * Generate a message id, based on the `key` and `seqno`
22
- */
23
- export const msgId = (key: PublicKey, seqno: bigint): Uint8Array => {
24
- const seqnoBytes = uint8ArrayFromString(seqno.toString(16).padStart(16, '0'), 'base16')
25
- const keyBytes = publicKeyToProtobuf(key)
26
-
27
- const msgId = new Uint8Array(keyBytes.byteLength + seqnoBytes.length)
28
- msgId.set(keyBytes, 0)
29
- msgId.set(seqnoBytes, keyBytes.byteLength)
30
-
31
- return msgId
32
- }
33
-
34
- /**
35
- * Generate a message id, based on message `data`
36
- */
37
- export const noSignMsgId = (data: Uint8Array): Uint8Array | Promise<Uint8Array> => {
38
- return sha256.encode(data)
39
- }
40
-
41
- /**
42
- * Check if any member of the first set is also a member
43
- * of the second set
44
- */
45
- export const anyMatch = (a: Set<number> | number[], b: Set<number> | number[]): boolean => {
46
- let bHas
47
- if (Array.isArray(b)) {
48
- bHas = (val: number) => b.includes(val)
49
- } else {
50
- bHas = (val: number) => b.has(val)
51
- }
52
-
53
- for (const val of a) {
54
- if (bHas(val)) {
55
- return true
56
- }
57
- }
58
-
59
- return false
60
- }
61
-
62
- /**
63
- * Make everything an array
64
- */
65
- export const ensureArray = function <T> (maybeArray: T | T[]): T[] {
66
- if (!Array.isArray(maybeArray)) {
67
- return [maybeArray]
68
- }
69
-
70
- return maybeArray
71
- }
72
-
73
- const isSigned = async (message: PubSubRPCMessage): Promise<boolean> => {
74
- if ((message.sequenceNumber == null) || (message.from == null) || (message.signature == null)) {
75
- return false
76
- }
77
- // if a public key is present in the `from` field, the message should be signed
78
- const fromID = peerIdFromMultihash(Digest.decode(message.from))
79
- if (fromID.publicKey != null) {
80
- return true
81
- }
82
-
83
- if (message.key != null) {
84
- const signingKey = message.key
85
- const signingID = peerIdFromPublicKey(publicKeyFromProtobuf(signingKey))
86
-
87
- return signingID.equals(fromID)
88
- }
89
-
90
- return false
91
- }
92
-
93
- export const toMessage = async (message: PubSubRPCMessage): Promise<Message> => {
94
- if (message.from == null) {
95
- throw new InvalidMessageError('RPC message was missing from')
96
- }
97
-
98
- if (!await isSigned(message)) {
99
- return {
100
- type: 'unsigned',
101
- topic: message.topic ?? '',
102
- data: message.data ?? new Uint8Array(0)
103
- }
104
- }
105
-
106
- const from = peerIdFromMultihash(Digest.decode(message.from))
107
- const key = message.key ?? from.publicKey
108
-
109
- if (key == null) {
110
- throw new InvalidMessageError('RPC message was missing public key')
111
- }
112
-
113
- const msg: Message = {
114
- type: 'signed',
115
- from,
116
- topic: message.topic ?? '',
117
- sequenceNumber: bigIntFromBytes(message.sequenceNumber ?? new Uint8Array(0)),
118
- data: message.data ?? new Uint8Array(0),
119
- signature: message.signature ?? new Uint8Array(0),
120
- key: key instanceof Uint8Array ? publicKeyFromProtobuf(key) : key
121
- }
122
-
123
- return msg
124
- }
125
-
126
- export const toRpcMessage = (message: Message): PubSubRPCMessage => {
127
- if (message.type === 'signed') {
128
- return {
129
- from: message.from.toMultihash().bytes,
130
- data: message.data,
131
- sequenceNumber: bigIntToBytes(message.sequenceNumber),
132
- topic: message.topic,
133
- signature: message.signature,
134
-
135
- key: message.key ? publicKeyToProtobuf(message.key) : undefined
136
- }
137
- }
138
-
139
- return {
140
- data: message.data,
141
- topic: message.topic
142
- }
143
- }
144
-
145
- export const bigIntToBytes = (num: bigint): Uint8Array => {
146
- let str = num.toString(16)
147
-
148
- if (str.length % 2 !== 0) {
149
- str = `0${str}`
150
- }
151
-
152
- return uint8ArrayFromString(str, 'base16')
153
- }
154
-
155
- export const bigIntFromBytes = (num: Uint8Array): bigint => {
156
- return BigInt(`0x${uint8ArrayToString(num, 'base16')}`)
157
- }