@waku/core 0.0.34-c41b319.0 → 0.0.34-c43cec2.0

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.
Files changed (60) hide show
  1. package/bundle/{base_protocol-CCK9RCtH.js → base_protocol-DxFKDXX2.js} +20 -19
  2. package/bundle/{index-Db7LxDrL.js → index-yLOEQnIE.js} +39 -75
  3. package/bundle/index.js +133 -1815
  4. package/bundle/lib/base_protocol.js +2 -2
  5. package/bundle/lib/message/version_0.js +2 -2
  6. package/bundle/{version_0-ANFNAdFD.js → version_0-Che4t3mN.js} +42 -163
  7. package/dist/.tsbuildinfo +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/base_protocol.d.ts +2 -2
  12. package/dist/lib/base_protocol.js +2 -2
  13. package/dist/lib/base_protocol.js.map +1 -1
  14. package/dist/lib/connection_manager/connection_manager.d.ts +3 -56
  15. package/dist/lib/connection_manager/connection_manager.js +13 -96
  16. package/dist/lib/connection_manager/connection_manager.js.map +1 -1
  17. package/dist/lib/filter/index.d.ts +18 -1
  18. package/dist/lib/filter/index.js +208 -1
  19. package/dist/lib/filter/index.js.map +1 -1
  20. package/dist/lib/health_manager.d.ts +14 -0
  21. package/dist/lib/health_manager.js +70 -0
  22. package/dist/lib/health_manager.js.map +1 -0
  23. package/dist/lib/light_push/index.d.ts +15 -1
  24. package/dist/lib/light_push/index.js +144 -1
  25. package/dist/lib/light_push/index.js.map +1 -1
  26. package/dist/lib/metadata/index.d.ts +3 -1
  27. package/dist/lib/metadata/index.js +118 -1
  28. package/dist/lib/metadata/index.js.map +1 -1
  29. package/dist/lib/store/index.d.ts +9 -1
  30. package/dist/lib/store/index.js +82 -1
  31. package/dist/lib/store/index.js.map +1 -1
  32. package/dist/lib/stream_manager/stream_manager.d.ts +2 -2
  33. package/dist/lib/stream_manager/stream_manager.js +17 -16
  34. package/dist/lib/stream_manager/stream_manager.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/index.ts +2 -0
  37. package/src/lib/base_protocol.ts +3 -3
  38. package/src/lib/connection_manager/connection_manager.ts +20 -114
  39. package/src/lib/filter/index.ts +315 -1
  40. package/src/lib/health_manager.ts +90 -0
  41. package/src/lib/light_push/index.ts +189 -1
  42. package/src/lib/metadata/index.ts +182 -1
  43. package/src/lib/store/index.ts +136 -1
  44. package/src/lib/stream_manager/stream_manager.ts +18 -16
  45. package/dist/lib/filter/filter.d.ts +0 -18
  46. package/dist/lib/filter/filter.js +0 -209
  47. package/dist/lib/filter/filter.js.map +0 -1
  48. package/dist/lib/light_push/light_push.d.ts +0 -15
  49. package/dist/lib/light_push/light_push.js +0 -144
  50. package/dist/lib/light_push/light_push.js.map +0 -1
  51. package/dist/lib/metadata/metadata.d.ts +0 -3
  52. package/dist/lib/metadata/metadata.js +0 -119
  53. package/dist/lib/metadata/metadata.js.map +0 -1
  54. package/dist/lib/store/store.d.ts +0 -9
  55. package/dist/lib/store/store.js +0 -83
  56. package/dist/lib/store/store.js.map +0 -1
  57. package/src/lib/filter/filter.ts +0 -315
  58. package/src/lib/light_push/light_push.ts +0 -188
  59. package/src/lib/metadata/metadata.ts +0 -182
  60. package/src/lib/store/store.ts +0 -136
@@ -1,315 +0,0 @@
1
- import type { PeerId, Stream } from "@libp2p/interface";
2
- import type { IncomingStreamData } from "@libp2p/interface-internal";
3
- import {
4
- type ContentTopic,
5
- type CoreProtocolResult,
6
- type IBaseProtocolCore,
7
- type Libp2p,
8
- ProtocolError,
9
- type PubsubTopic
10
- } from "@waku/interfaces";
11
- import { WakuMessage } from "@waku/proto";
12
- import { Logger } from "@waku/utils";
13
- import all from "it-all";
14
- import * as lp from "it-length-prefixed";
15
- import { pipe } from "it-pipe";
16
- import { Uint8ArrayList } from "uint8arraylist";
17
-
18
- import { BaseProtocol } from "../base_protocol.js";
19
-
20
- import {
21
- FilterPushRpc,
22
- FilterSubscribeResponse,
23
- FilterSubscribeRpc
24
- } from "./filter_rpc.js";
25
-
26
- const log = new Logger("filter:v2");
27
-
28
- export const FilterCodecs = {
29
- SUBSCRIBE: "/vac/waku/filter-subscribe/2.0.0-beta1",
30
- PUSH: "/vac/waku/filter-push/2.0.0-beta1"
31
- };
32
-
33
- export class FilterCore extends BaseProtocol implements IBaseProtocolCore {
34
- public constructor(
35
- private handleIncomingMessage: (
36
- pubsubTopic: PubsubTopic,
37
- wakuMessage: WakuMessage,
38
- peerIdStr: string
39
- ) => Promise<void>,
40
- public readonly pubsubTopics: PubsubTopic[],
41
- libp2p: Libp2p
42
- ) {
43
- super(FilterCodecs.SUBSCRIBE, libp2p.components, pubsubTopics);
44
-
45
- libp2p
46
- .handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
47
- maxInboundStreams: 100
48
- })
49
- .catch((e) => {
50
- log.error("Failed to register ", FilterCodecs.PUSH, e);
51
- });
52
- }
53
-
54
- public async subscribe(
55
- pubsubTopic: PubsubTopic,
56
- peerId: PeerId,
57
- contentTopics: ContentTopic[]
58
- ): Promise<CoreProtocolResult> {
59
- const stream = await this.getStream(peerId);
60
-
61
- const request = FilterSubscribeRpc.createSubscribeRequest(
62
- pubsubTopic,
63
- contentTopics
64
- );
65
-
66
- let res: Uint8ArrayList[] | undefined;
67
- try {
68
- res = await pipe(
69
- [request.encode()],
70
- lp.encode,
71
- stream,
72
- lp.decode,
73
- async (source) => await all(source)
74
- );
75
- } catch (error) {
76
- log.error("Failed to send subscribe request", error);
77
- return {
78
- success: null,
79
- failure: {
80
- error: ProtocolError.GENERIC_FAIL,
81
- peerId: peerId
82
- }
83
- };
84
- }
85
-
86
- const { statusCode, requestId, statusDesc } =
87
- FilterSubscribeResponse.decode(res[0].slice());
88
-
89
- if (statusCode < 200 || statusCode >= 300) {
90
- log.error(
91
- `Filter subscribe request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
92
- );
93
- return {
94
- failure: {
95
- error: ProtocolError.REMOTE_PEER_REJECTED,
96
- peerId: peerId
97
- },
98
- success: null
99
- };
100
- }
101
-
102
- return {
103
- failure: null,
104
- success: peerId
105
- };
106
- }
107
-
108
- public async unsubscribe(
109
- pubsubTopic: PubsubTopic,
110
- peerId: PeerId,
111
- contentTopics: ContentTopic[]
112
- ): Promise<CoreProtocolResult> {
113
- let stream: Stream | undefined;
114
- try {
115
- stream = await this.getStream(peerId);
116
- } catch (error) {
117
- log.error(
118
- `Failed to get a stream for remote peer${peerId.toString()}`,
119
- error
120
- );
121
- return {
122
- success: null,
123
- failure: {
124
- error: ProtocolError.NO_STREAM_AVAILABLE,
125
- peerId: peerId
126
- }
127
- };
128
- }
129
-
130
- const unsubscribeRequest = FilterSubscribeRpc.createUnsubscribeRequest(
131
- pubsubTopic,
132
- contentTopics
133
- );
134
-
135
- try {
136
- await pipe([unsubscribeRequest.encode()], lp.encode, stream.sink);
137
- } catch (error) {
138
- log.error("Failed to send unsubscribe request", error);
139
- return {
140
- success: null,
141
- failure: {
142
- error: ProtocolError.GENERIC_FAIL,
143
- peerId: peerId
144
- }
145
- };
146
- }
147
-
148
- return {
149
- success: peerId,
150
- failure: null
151
- };
152
- }
153
-
154
- public async unsubscribeAll(
155
- pubsubTopic: PubsubTopic,
156
- peerId: PeerId
157
- ): Promise<CoreProtocolResult> {
158
- const stream = await this.getStream(peerId);
159
-
160
- const request = FilterSubscribeRpc.createUnsubscribeAllRequest(pubsubTopic);
161
-
162
- const res = await pipe(
163
- [request.encode()],
164
- lp.encode,
165
- stream,
166
- lp.decode,
167
- async (source) => await all(source)
168
- );
169
-
170
- if (!res || !res.length) {
171
- return {
172
- failure: {
173
- error: ProtocolError.NO_RESPONSE,
174
- peerId: peerId
175
- },
176
- success: null
177
- };
178
- }
179
-
180
- const { statusCode, requestId, statusDesc } =
181
- FilterSubscribeResponse.decode(res[0].slice());
182
-
183
- if (statusCode < 200 || statusCode >= 300) {
184
- log.error(
185
- `Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
186
- );
187
- return {
188
- failure: {
189
- error: ProtocolError.REMOTE_PEER_REJECTED,
190
- peerId: peerId
191
- },
192
- success: null
193
- };
194
- }
195
-
196
- return {
197
- failure: null,
198
- success: peerId
199
- };
200
- }
201
-
202
- public async ping(peerId: PeerId): Promise<CoreProtocolResult> {
203
- let stream: Stream | undefined;
204
- try {
205
- stream = await this.getStream(peerId);
206
- } catch (error) {
207
- log.error(
208
- `Failed to get a stream for remote peer${peerId.toString()}`,
209
- error
210
- );
211
- return {
212
- success: null,
213
- failure: {
214
- error: ProtocolError.NO_STREAM_AVAILABLE,
215
- peerId: peerId
216
- }
217
- };
218
- }
219
-
220
- const request = FilterSubscribeRpc.createSubscriberPingRequest();
221
-
222
- let res: Uint8ArrayList[] | undefined;
223
- try {
224
- res = await pipe(
225
- [request.encode()],
226
- lp.encode,
227
- stream,
228
- lp.decode,
229
- async (source) => await all(source)
230
- );
231
- } catch (error) {
232
- log.error("Failed to send ping request", error);
233
- return {
234
- success: null,
235
- failure: {
236
- error: ProtocolError.GENERIC_FAIL,
237
- peerId: peerId
238
- }
239
- };
240
- }
241
-
242
- if (!res || !res.length) {
243
- return {
244
- success: null,
245
- failure: {
246
- error: ProtocolError.NO_RESPONSE,
247
- peerId: peerId
248
- }
249
- };
250
- }
251
-
252
- const { statusCode, requestId, statusDesc } =
253
- FilterSubscribeResponse.decode(res[0].slice());
254
-
255
- if (statusCode < 200 || statusCode >= 300) {
256
- log.error(
257
- `Filter ping request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
258
- );
259
- return {
260
- success: null,
261
- failure: {
262
- error: ProtocolError.REMOTE_PEER_REJECTED,
263
- peerId: peerId
264
- }
265
- };
266
- }
267
- return {
268
- success: peerId,
269
- failure: null
270
- };
271
- }
272
-
273
- private onRequest(streamData: IncomingStreamData): void {
274
- const { connection, stream } = streamData;
275
- const { remotePeer } = connection;
276
- log.info(`Received message from ${remotePeer.toString()}`);
277
- try {
278
- pipe(stream, lp.decode, async (source) => {
279
- for await (const bytes of source) {
280
- const response = FilterPushRpc.decode(bytes.slice());
281
-
282
- const { pubsubTopic, wakuMessage } = response;
283
-
284
- if (!wakuMessage) {
285
- log.error("Received empty message");
286
- return;
287
- }
288
-
289
- if (!pubsubTopic) {
290
- log.error("Pubsub topic missing from push message");
291
- return;
292
- }
293
-
294
- await this.handleIncomingMessage(
295
- pubsubTopic,
296
- wakuMessage,
297
- connection.remotePeer.toString()
298
- );
299
- }
300
- }).then(
301
- () => {
302
- log.info("Receiving pipe closed.");
303
- },
304
- async (e) => {
305
- log.error(
306
- `Error with receiving pipe on peer:${connection.remotePeer.toString()} -- stream:${stream.id} -- protocol:${stream.protocol}: `,
307
- e
308
- );
309
- }
310
- );
311
- } catch (e) {
312
- log.error("Error decoding message", e);
313
- }
314
- }
315
- }
@@ -1,188 +0,0 @@
1
- import type { PeerId, Stream } from "@libp2p/interface";
2
- import {
3
- type CoreProtocolResult,
4
- type IBaseProtocolCore,
5
- type IEncoder,
6
- type IMessage,
7
- type Libp2p,
8
- ProtocolError,
9
- PubsubTopic,
10
- type ThisOrThat
11
- } from "@waku/interfaces";
12
- import { PushResponse } from "@waku/proto";
13
- import { isMessageSizeUnderCap } from "@waku/utils";
14
- import { Logger } from "@waku/utils";
15
- import all from "it-all";
16
- import * as lp from "it-length-prefixed";
17
- import { pipe } from "it-pipe";
18
- import { Uint8ArrayList } from "uint8arraylist";
19
-
20
- import { BaseProtocol } from "../base_protocol.js";
21
-
22
- import { PushRpc } from "./push_rpc.js";
23
- import { isRLNResponseError, matchRLNErrorMessage } from "./utils.js";
24
-
25
- const log = new Logger("light-push");
26
-
27
- export const LightPushCodec = "/vac/waku/lightpush/2.0.0-beta1";
28
- export { PushResponse };
29
-
30
- type PreparePushMessageResult = ThisOrThat<"query", PushRpc>;
31
-
32
- /**
33
- * Implements the [Waku v2 Light Push protocol](https://rfc.vac.dev/spec/19/).
34
- */
35
- export class LightPushCore extends BaseProtocol implements IBaseProtocolCore {
36
- public constructor(
37
- public readonly pubsubTopics: PubsubTopic[],
38
- libp2p: Libp2p
39
- ) {
40
- super(LightPushCodec, libp2p.components, pubsubTopics);
41
- }
42
-
43
- private async preparePushMessage(
44
- encoder: IEncoder,
45
- message: IMessage
46
- ): Promise<PreparePushMessageResult> {
47
- try {
48
- if (!message.payload || message.payload.length === 0) {
49
- log.error("Failed to send waku light push: payload is empty");
50
- return { query: null, error: ProtocolError.EMPTY_PAYLOAD };
51
- }
52
-
53
- if (!(await isMessageSizeUnderCap(encoder, message))) {
54
- log.error("Failed to send waku light push: message is bigger than 1MB");
55
- return { query: null, error: ProtocolError.SIZE_TOO_BIG };
56
- }
57
-
58
- const protoMessage = await encoder.toProtoObj(message);
59
- if (!protoMessage) {
60
- log.error("Failed to encode to protoMessage, aborting push");
61
- return {
62
- query: null,
63
- error: ProtocolError.ENCODE_FAILED
64
- };
65
- }
66
-
67
- const query = PushRpc.createRequest(protoMessage, encoder.pubsubTopic);
68
- return { query, error: null };
69
- } catch (error) {
70
- log.error("Failed to prepare push message", error);
71
-
72
- return {
73
- query: null,
74
- error: ProtocolError.GENERIC_FAIL
75
- };
76
- }
77
- }
78
-
79
- public async send(
80
- encoder: IEncoder,
81
- message: IMessage,
82
- peerId: PeerId
83
- ): Promise<CoreProtocolResult> {
84
- const { query, error: preparationError } = await this.preparePushMessage(
85
- encoder,
86
- message
87
- );
88
-
89
- if (preparationError || !query) {
90
- return {
91
- success: null,
92
- failure: {
93
- error: preparationError,
94
- peerId
95
- }
96
- };
97
- }
98
-
99
- let stream: Stream;
100
- try {
101
- stream = await this.getStream(peerId);
102
- } catch (error) {
103
- log.error("Failed to get stream", error);
104
- return {
105
- success: null,
106
- failure: {
107
- error: ProtocolError.NO_STREAM_AVAILABLE,
108
- peerId: peerId
109
- }
110
- };
111
- }
112
-
113
- let res: Uint8ArrayList[] | undefined;
114
- try {
115
- res = await pipe(
116
- [query.encode()],
117
- lp.encode,
118
- stream,
119
- lp.decode,
120
- async (source) => await all(source)
121
- );
122
- } catch (err) {
123
- log.error("Failed to send waku light push request", err);
124
- return {
125
- success: null,
126
- failure: {
127
- error: ProtocolError.GENERIC_FAIL,
128
- peerId: peerId
129
- }
130
- };
131
- }
132
-
133
- const bytes = new Uint8ArrayList();
134
- res.forEach((chunk) => {
135
- bytes.append(chunk);
136
- });
137
-
138
- let response: PushResponse | undefined;
139
- try {
140
- response = PushRpc.decode(bytes).response;
141
- } catch (err) {
142
- log.error("Failed to decode push reply", err);
143
- return {
144
- success: null,
145
- failure: {
146
- error: ProtocolError.DECODE_FAILED,
147
- peerId: peerId
148
- }
149
- };
150
- }
151
-
152
- if (!response) {
153
- log.error("Remote peer fault: No response in PushRPC");
154
- return {
155
- success: null,
156
- failure: {
157
- error: ProtocolError.NO_RESPONSE,
158
- peerId: peerId
159
- }
160
- };
161
- }
162
-
163
- if (isRLNResponseError(response.info)) {
164
- const rlnErrorCase = matchRLNErrorMessage(response.info!);
165
- log.error("Remote peer rejected the message: ", rlnErrorCase);
166
- return {
167
- success: null,
168
- failure: {
169
- error: rlnErrorCase,
170
- peerId: peerId
171
- }
172
- };
173
- }
174
-
175
- if (!response.isSuccess) {
176
- log.error("Remote peer rejected the message: ", response.info);
177
- return {
178
- success: null,
179
- failure: {
180
- error: ProtocolError.REMOTE_PEER_REJECTED,
181
- peerId: peerId
182
- }
183
- };
184
- }
185
-
186
- return { success: peerId, failure: null };
187
- }
188
- }
@@ -1,182 +0,0 @@
1
- import type { PeerId } from "@libp2p/interface";
2
- import { IncomingStreamData } from "@libp2p/interface";
3
- import {
4
- type IMetadata,
5
- type Libp2pComponents,
6
- type MetadataQueryResult,
7
- type PeerIdStr,
8
- ProtocolError,
9
- PubsubTopic,
10
- type ShardInfo
11
- } from "@waku/interfaces";
12
- import { proto_metadata } from "@waku/proto";
13
- import { encodeRelayShard, Logger, pubsubTopicsToShardInfo } from "@waku/utils";
14
- import all from "it-all";
15
- import * as lp from "it-length-prefixed";
16
- import { pipe } from "it-pipe";
17
- import { Uint8ArrayList } from "uint8arraylist";
18
-
19
- import { BaseProtocol } from "../base_protocol.js";
20
-
21
- const log = new Logger("metadata");
22
-
23
- export const MetadataCodec = "/vac/waku/metadata/1.0.0";
24
-
25
- class Metadata extends BaseProtocol implements IMetadata {
26
- private libp2pComponents: Libp2pComponents;
27
- protected handshakesConfirmed: Map<PeerIdStr, ShardInfo> = new Map();
28
-
29
- public constructor(
30
- public pubsubTopics: PubsubTopic[],
31
- libp2p: Libp2pComponents
32
- ) {
33
- super(MetadataCodec, libp2p.components, pubsubTopics);
34
- this.libp2pComponents = libp2p;
35
- void libp2p.registrar.handle(MetadataCodec, (streamData) => {
36
- void this.onRequest(streamData);
37
- });
38
- }
39
-
40
- /**
41
- * Make a metadata query to a peer
42
- */
43
- public async query(peerId: PeerId): Promise<MetadataQueryResult> {
44
- const request = proto_metadata.WakuMetadataRequest.encode(
45
- pubsubTopicsToShardInfo(this.pubsubTopics)
46
- );
47
-
48
- const peer = await this.libp2pComponents.peerStore.get(peerId);
49
- if (!peer) {
50
- return {
51
- shardInfo: null,
52
- error: ProtocolError.NO_PEER_AVAILABLE
53
- };
54
- }
55
-
56
- let stream;
57
- try {
58
- stream = await this.getStream(peerId);
59
- } catch (error) {
60
- log.error("Failed to get stream", error);
61
- return {
62
- shardInfo: null,
63
- error: ProtocolError.NO_STREAM_AVAILABLE
64
- };
65
- }
66
-
67
- const encodedResponse = await pipe(
68
- [request],
69
- lp.encode,
70
- stream,
71
- lp.decode,
72
- async (source) => await all(source)
73
- );
74
-
75
- const { error, shardInfo } = this.decodeMetadataResponse(encodedResponse);
76
-
77
- if (error) {
78
- return {
79
- shardInfo: null,
80
- error
81
- };
82
- }
83
-
84
- await this.savePeerShardInfo(peerId, shardInfo);
85
-
86
- return {
87
- shardInfo,
88
- error: null
89
- };
90
- }
91
-
92
- public async confirmOrAttemptHandshake(
93
- peerId: PeerId
94
- ): Promise<MetadataQueryResult> {
95
- const shardInfo = this.handshakesConfirmed.get(peerId.toString());
96
- if (shardInfo) {
97
- return {
98
- shardInfo,
99
- error: null
100
- };
101
- }
102
-
103
- return await this.query(peerId);
104
- }
105
-
106
- /**
107
- * Handle an incoming metadata request
108
- */
109
- private async onRequest(streamData: IncomingStreamData): Promise<void> {
110
- try {
111
- const { stream, connection } = streamData;
112
- const encodedShardInfo = proto_metadata.WakuMetadataResponse.encode(
113
- pubsubTopicsToShardInfo(this.pubsubTopics)
114
- );
115
-
116
- const encodedResponse = await pipe(
117
- [encodedShardInfo],
118
- lp.encode,
119
- stream,
120
- lp.decode,
121
- async (source) => await all(source)
122
- );
123
-
124
- const { error, shardInfo } = this.decodeMetadataResponse(encodedResponse);
125
-
126
- if (error) {
127
- return;
128
- }
129
-
130
- await this.savePeerShardInfo(connection.remotePeer, shardInfo);
131
- } catch (error) {
132
- log.error("Error handling metadata request", error);
133
- }
134
- }
135
-
136
- private decodeMetadataResponse(
137
- encodedResponse: Uint8ArrayList[]
138
- ): MetadataQueryResult {
139
- const bytes = new Uint8ArrayList();
140
-
141
- encodedResponse.forEach((chunk) => {
142
- bytes.append(chunk);
143
- });
144
- const response = proto_metadata.WakuMetadataResponse.decode(
145
- bytes
146
- ) as ShardInfo;
147
-
148
- if (!response) {
149
- log.error("Error decoding metadata response");
150
- return {
151
- shardInfo: null,
152
- error: ProtocolError.DECODE_FAILED
153
- };
154
- }
155
-
156
- return {
157
- shardInfo: response,
158
- error: null
159
- };
160
- }
161
-
162
- private async savePeerShardInfo(
163
- peerId: PeerId,
164
- shardInfo: ShardInfo
165
- ): Promise<void> {
166
- // add or update the shardInfo to peer store
167
- await this.libp2pComponents.peerStore.merge(peerId, {
168
- metadata: {
169
- shardInfo: encodeRelayShard(shardInfo)
170
- }
171
- });
172
-
173
- this.handshakesConfirmed.set(peerId.toString(), shardInfo);
174
- }
175
- }
176
-
177
- export function wakuMetadata(
178
- pubsubTopics: PubsubTopic[]
179
- ): (components: Libp2pComponents) => IMetadata {
180
- return (components: Libp2pComponents) =>
181
- new Metadata(pubsubTopics, components);
182
- }