@streamr/trackerless-network 100.0.0-testnet-two.1 → 100.0.0-testnet-two.3

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 (120) hide show
  1. package/dist/package.json +6 -6
  2. package/dist/src/NetworkNode.d.ts +3 -4
  3. package/dist/src/NetworkNode.js.map +1 -1
  4. package/dist/src/NetworkStack.js +1 -1
  5. package/dist/src/NetworkStack.js.map +1 -1
  6. package/dist/src/exports.d.ts +0 -1
  7. package/dist/src/exports.js.map +1 -1
  8. package/dist/src/logic/DeliveryRpcLocal.d.ts +4 -5
  9. package/dist/src/logic/DeliveryRpcLocal.js +3 -3
  10. package/dist/src/logic/DeliveryRpcLocal.js.map +1 -1
  11. package/dist/src/logic/DeliveryRpcRemote.d.ts +2 -1
  12. package/dist/src/logic/DeliveryRpcRemote.js +2 -2
  13. package/dist/src/logic/DeliveryRpcRemote.js.map +1 -1
  14. package/dist/src/logic/EntryPointDiscovery.d.ts +5 -5
  15. package/dist/src/logic/EntryPointDiscovery.js +4 -5
  16. package/dist/src/logic/EntryPointDiscovery.js.map +1 -1
  17. package/dist/src/logic/Layer0Node.d.ts +4 -4
  18. package/dist/src/logic/NodeList.d.ts +12 -13
  19. package/dist/src/logic/NodeList.js +4 -4
  20. package/dist/src/logic/NodeList.js.map +1 -1
  21. package/dist/src/logic/RandomGraphNode.d.ts +6 -7
  22. package/dist/src/logic/RandomGraphNode.js +9 -9
  23. package/dist/src/logic/RandomGraphNode.js.map +1 -1
  24. package/dist/src/logic/StreamrNode.d.ts +3 -4
  25. package/dist/src/logic/StreamrNode.js +4 -4
  26. package/dist/src/logic/StreamrNode.js.map +1 -1
  27. package/dist/src/logic/createRandomGraphNode.js +1 -3
  28. package/dist/src/logic/createRandomGraphNode.js.map +1 -1
  29. package/dist/src/logic/inspect/InspectSession.d.ts +3 -3
  30. package/dist/src/logic/inspect/InspectSession.js.map +1 -1
  31. package/dist/src/logic/inspect/Inspector.d.ts +3 -4
  32. package/dist/src/logic/inspect/Inspector.js +3 -3
  33. package/dist/src/logic/inspect/Inspector.js.map +1 -1
  34. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.d.ts +4 -5
  35. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js +11 -10
  36. package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js.map +1 -1
  37. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.d.ts +3 -3
  38. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js +7 -8
  39. package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js.map +1 -1
  40. package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +3 -4
  41. package/dist/src/logic/neighbor-discovery/Handshaker.js +12 -12
  42. package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -1
  43. package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +3 -3
  44. package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
  45. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +4 -4
  46. package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
  47. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js +6 -7
  48. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js.map +1 -1
  49. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.d.ts +2 -1
  50. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js +3 -4
  51. package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js.map +1 -1
  52. package/dist/src/logic/propagation/Propagation.d.ts +4 -4
  53. package/dist/src/logic/propagation/Propagation.js.map +1 -1
  54. package/dist/src/logic/propagation/PropagationTaskStore.d.ts +2 -2
  55. package/dist/src/logic/proxy/ProxyClient.d.ts +3 -4
  56. package/dist/src/logic/proxy/ProxyClient.js +8 -9
  57. package/dist/src/logic/proxy/ProxyClient.js.map +1 -1
  58. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.d.ts +6 -7
  59. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js +4 -4
  60. package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js.map +1 -1
  61. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.d.ts +0 -2
  62. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js +3 -3
  63. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js.map +1 -1
  64. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js +1 -2
  65. package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js.map +1 -1
  66. package/dist/test/benchmark/first-message.js +2 -3
  67. package/dist/test/benchmark/first-message.js.map +1 -1
  68. package/dist/test/utils/utils.d.ts +2 -3
  69. package/dist/test/utils/utils.js +4 -4
  70. package/dist/test/utils/utils.js.map +1 -1
  71. package/package.json +6 -6
  72. package/src/NetworkNode.ts +3 -4
  73. package/src/NetworkStack.ts +1 -1
  74. package/src/exports.ts +0 -1
  75. package/src/logic/DeliveryRpcLocal.ts +4 -5
  76. package/src/logic/DeliveryRpcRemote.ts +3 -2
  77. package/src/logic/EntryPointDiscovery.ts +11 -9
  78. package/src/logic/Layer0Node.ts +4 -4
  79. package/src/logic/NodeList.ts +15 -16
  80. package/src/logic/RandomGraphNode.ts +16 -17
  81. package/src/logic/StreamrNode.ts +8 -6
  82. package/src/logic/createRandomGraphNode.ts +2 -4
  83. package/src/logic/inspect/InspectSession.ts +4 -4
  84. package/src/logic/inspect/Inspector.ts +4 -6
  85. package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +20 -12
  86. package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +11 -10
  87. package/src/logic/neighbor-discovery/Handshaker.ts +14 -15
  88. package/src/logic/neighbor-discovery/NeighborFinder.ts +4 -4
  89. package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +2 -4
  90. package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +6 -9
  91. package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +4 -4
  92. package/src/logic/propagation/Propagation.ts +6 -6
  93. package/src/logic/propagation/PropagationTaskStore.ts +2 -2
  94. package/src/logic/proxy/ProxyClient.ts +20 -21
  95. package/src/logic/proxy/ProxyConnectionRpcLocal.ts +10 -12
  96. package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +1 -5
  97. package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +1 -2
  98. package/test/benchmark/first-message.ts +2 -3
  99. package/test/end-to-end/proxy-connections.test.ts +2 -2
  100. package/test/end-to-end/webrtc-full-node-network.test.ts +1 -2
  101. package/test/end-to-end/websocket-full-node-network.test.ts +1 -1
  102. package/test/integration/DeliveryRpcRemote.test.ts +2 -5
  103. package/test/integration/HandshakeRpcRemote.test.ts +2 -2
  104. package/test/integration/Handshakes.test.ts +2 -6
  105. package/test/integration/NeighborUpdateRpcRemote.test.ts +1 -2
  106. package/test/unit/HandshakeRpcLocal.test.ts +9 -11
  107. package/test/unit/Handshaker.test.ts +1 -2
  108. package/test/unit/InspectSession.test.ts +3 -3
  109. package/test/unit/Inspector.test.ts +1 -2
  110. package/test/unit/NeighborFinder.test.ts +2 -2
  111. package/test/unit/NodeList.test.ts +12 -13
  112. package/test/unit/Propagation.test.ts +9 -9
  113. package/test/unit/ProxyConnectionRpcRemote.test.ts +0 -2
  114. package/test/unit/RandomGraphNode.test.ts +1 -1
  115. package/test/utils/mock/MockHandshaker.ts +3 -3
  116. package/test/utils/utils.ts +15 -7
  117. package/dist/src/identifiers.d.ts +0 -4
  118. package/dist/src/identifiers.js +0 -9
  119. package/dist/src/identifiers.js.map +0 -1
  120. package/src/identifiers.ts +0 -8
@@ -6,24 +6,31 @@ import {
6
6
  } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
7
7
  import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
8
8
  import { NodeList } from '../NodeList'
9
- import { ConnectionLocker, DhtCallContext, PeerDescriptor } from '@streamr/dht'
9
+ import {
10
+ ConnectionLocker,
11
+ DhtAddress,
12
+ DhtAddressRaw,
13
+ DhtCallContext,
14
+ PeerDescriptor,
15
+ getDhtAddressFromRaw,
16
+ getNodeIdFromPeerDescriptor
17
+ } from '@streamr/dht'
10
18
  import { IHandshakeRpc } from '../../proto/packages/trackerless-network/protos/NetworkRpc.server'
11
19
  import { HandshakeRpcRemote } from './HandshakeRpcRemote'
12
20
  import { DeliveryRpcRemote } from '../DeliveryRpcRemote'
13
- import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
14
- import { Logger, binaryToHex } from '@streamr/utils'
21
+ import { Logger } from '@streamr/utils'
15
22
  import { StreamPartID } from '@streamr/protocol'
16
23
 
17
24
  interface HandshakeRpcLocalConfig {
18
25
  streamPartId: StreamPartID
19
26
  targetNeighbors: NodeList
20
27
  connectionLocker: ConnectionLocker
21
- ongoingHandshakes: Set<NodeID>
22
- ongoingInterleaves: Set<NodeID>
28
+ ongoingHandshakes: Set<DhtAddress>
29
+ ongoingInterleaves: Set<DhtAddress>
23
30
  maxNeighborCount: number
24
31
  createRpcRemote: (target: PeerDescriptor) => HandshakeRpcRemote
25
32
  createDeliveryRpcRemote: (peerDescriptor: PeerDescriptor) => DeliveryRpcRemote
26
- handshakeWithInterleaving: (target: PeerDescriptor, senderId: NodeID) => Promise<boolean>
33
+ handshakeWithInterleaving: (target: PeerDescriptor, senderId: DhtAddress) => Promise<boolean>
27
34
  }
28
35
 
29
36
  const logger = new Logger(module)
@@ -42,11 +49,12 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
42
49
 
43
50
  private handleRequest(request: StreamPartHandshakeRequest, context: ServerCallContext): StreamPartHandshakeResponse {
44
51
  const senderDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
45
- const getInterleaveSourceIds = () => (request.interleaveSourceId !== undefined) ? [binaryToHex(request.interleaveSourceId) as NodeID] : []
46
- if (this.config.ongoingInterleaves.has(getNodeIdFromPeerDescriptor(senderDescriptor))) {
52
+ const getInterleaveSourceIds = () => (request.interleaveSourceId !== undefined) ? [getDhtAddressFromRaw(request.interleaveSourceId)] : []
53
+ const senderNodeId = getNodeIdFromPeerDescriptor(senderDescriptor)
54
+ if (this.config.ongoingInterleaves.has(senderNodeId)) {
47
55
  return this.rejectHandshake(request)
48
56
  } else if (this.config.targetNeighbors.hasNode(senderDescriptor)
49
- || this.config.ongoingHandshakes.has(getNodeIdFromPeerDescriptor(senderDescriptor))
57
+ || this.config.ongoingHandshakes.has(senderNodeId)
50
58
  ) {
51
59
  return this.acceptHandshake(request, senderDescriptor)
52
60
  } else if (this.config.targetNeighbors.size() + this.config.ongoingHandshakes.size < this.config.maxNeighborCount) {
@@ -80,12 +88,12 @@ export class HandshakeRpcLocal implements IHandshakeRpc {
80
88
  }
81
89
 
82
90
  private acceptHandshakeWithInterleaving(request: StreamPartHandshakeRequest, requester: PeerDescriptor): StreamPartHandshakeResponse {
83
- const exclude: NodeID[] = []
84
- request.neighborIds.forEach((id: Uint8Array) => exclude.push(binaryToHex(id) as NodeID))
91
+ const exclude: DhtAddress[] = []
92
+ request.neighborIds.forEach((id: DhtAddressRaw) => exclude.push(getDhtAddressFromRaw(id)))
85
93
  this.config.ongoingInterleaves.forEach((id) => exclude.push(id))
86
94
  exclude.push(getNodeIdFromPeerDescriptor(requester))
87
95
  if (request.interleaveSourceId !== undefined) {
88
- exclude.push(binaryToHex(request.interleaveSourceId) as NodeID)
96
+ exclude.push(getDhtAddressFromRaw(request.interleaveSourceId))
89
97
  }
90
98
  const furthest = this.config.targetNeighbors.getFurthest(exclude)
91
99
  const furthestPeerDescriptor = furthest ? furthest.getPeerDescriptor() : undefined
@@ -1,9 +1,9 @@
1
- import { PeerDescriptor, RpcRemote } from '@streamr/dht'
2
- import { Logger, hexToBinary } from '@streamr/utils'
1
+ import { DhtAddress, PeerDescriptor, RpcRemote, getNodeIdFromPeerDescriptor, getRawFromDhtAddress } from '@streamr/dht'
2
+ import { Logger } from '@streamr/utils'
3
3
  import { v4 } from 'uuid'
4
- import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
5
4
  import { InterleaveRequest, InterleaveResponse, StreamPartHandshakeRequest } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
6
5
  import { HandshakeRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
6
+ import { StreamPartID } from '@streamr/protocol'
7
7
 
8
8
  const logger = new Logger(module)
9
9
 
@@ -17,16 +17,17 @@ export const INTERLEAVE_REQUEST_TIMEOUT = 15000
17
17
  export class HandshakeRpcRemote extends RpcRemote<HandshakeRpcClient> {
18
18
 
19
19
  async handshake(
20
- neighborIds: NodeID[],
21
- concurrentHandshakeTargetId?: NodeID,
22
- interleaveSourceId?: NodeID
20
+ streamPartId: StreamPartID,
21
+ neighborIds: DhtAddress[],
22
+ concurrentHandshakeTargetId?: DhtAddress,
23
+ interleaveSourceId?: DhtAddress
23
24
  ): Promise<HandshakeResponse> {
24
25
  const request: StreamPartHandshakeRequest = {
25
- streamPartId: this.getServiceId(),
26
+ streamPartId,
26
27
  requestId: v4(),
27
- neighborIds: neighborIds.map((id) => hexToBinary(id)),
28
- concurrentHandshakeTargetId: (concurrentHandshakeTargetId !== undefined) ? hexToBinary(concurrentHandshakeTargetId) : undefined,
29
- interleaveSourceId: (interleaveSourceId !== undefined) ? hexToBinary(interleaveSourceId) : undefined
28
+ neighborIds: neighborIds.map((id) => getRawFromDhtAddress(id)),
29
+ concurrentHandshakeTargetId: (concurrentHandshakeTargetId !== undefined) ? getRawFromDhtAddress(concurrentHandshakeTargetId) : undefined,
30
+ interleaveSourceId: (interleaveSourceId !== undefined) ? getRawFromDhtAddress(interleaveSourceId) : undefined
30
31
  }
31
32
  try {
32
33
  const response = await this.getClient().handshake(request, this.formDhtRpcOptions())
@@ -1,4 +1,4 @@
1
- import { ConnectionLocker, PeerDescriptor } from '@streamr/dht'
1
+ import { ConnectionLocker, DhtAddress, PeerDescriptor, getNodeIdFromPeerDescriptor } from '@streamr/dht'
2
2
  import { NodeList } from '../NodeList'
3
3
  import { DeliveryRpcRemote } from '../DeliveryRpcRemote'
4
4
  import { RpcCommunicator } from '@streamr/proto-rpc'
@@ -15,7 +15,6 @@ import { Logger } from '@streamr/utils'
15
15
  import { IHandshakeRpc } from '../../proto/packages/trackerless-network/protos/NetworkRpc.server'
16
16
  import { HandshakeRpcRemote, INTERLEAVE_REQUEST_TIMEOUT } from './HandshakeRpcRemote'
17
17
  import { HandshakeRpcLocal } from './HandshakeRpcLocal'
18
- import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
19
18
  import { StreamPartID } from '@streamr/protocol'
20
19
 
21
20
  interface HandshakerConfig {
@@ -36,7 +35,7 @@ const PARALLEL_HANDSHAKE_COUNT = 2
36
35
 
37
36
  export class Handshaker {
38
37
 
39
- private readonly ongoingHandshakes: Set<NodeID> = new Set()
38
+ private readonly ongoingHandshakes: Set<DhtAddress> = new Set()
40
39
  private config: HandshakerConfig
41
40
  private readonly rpcLocal: IHandshakeRpc
42
41
 
@@ -49,7 +48,7 @@ export class Handshaker {
49
48
  ongoingHandshakes: this.ongoingHandshakes,
50
49
  ongoingInterleaves: new Set(),
51
50
  maxNeighborCount: this.config.maxNeighborCount,
52
- handshakeWithInterleaving: (target: PeerDescriptor, senderId: NodeID) => this.handshakeWithInterleaving(target, senderId),
51
+ handshakeWithInterleaving: (target: PeerDescriptor, senderId: DhtAddress) => this.handshakeWithInterleaving(target, senderId),
53
52
  createRpcRemote: (target: PeerDescriptor) => this.createRpcRemote(target),
54
53
  createDeliveryRpcRemote: (target: PeerDescriptor) => this.createDeliveryRpcRemote(target)
55
54
  })
@@ -59,7 +58,7 @@ export class Handshaker {
59
58
  (req: StreamPartHandshakeRequest, context) => this.rpcLocal.handshake(req, context))
60
59
  }
61
60
 
62
- async attemptHandshakesOnContacts(excludedIds: NodeID[]): Promise<NodeID[]> {
61
+ async attemptHandshakesOnContacts(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
63
62
  // TODO use config option or named constant? or why the value 2?
64
63
  if (this.config.targetNeighbors.size() + this.ongoingHandshakes.size < this.config.maxNeighborCount - 2) {
65
64
  logger.trace(`Attempting parallel handshakes with ${PARALLEL_HANDSHAKE_COUNT} targets`)
@@ -71,14 +70,14 @@ export class Handshaker {
71
70
  return excludedIds
72
71
  }
73
72
 
74
- private async selectParallelTargetsAndHandshake(excludedIds: NodeID[]): Promise<NodeID[]> {
73
+ private async selectParallelTargetsAndHandshake(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
75
74
  const exclude = excludedIds.concat(this.config.targetNeighbors.getIds())
76
75
  const targetNeighbors = this.selectParallelTargets(exclude)
77
76
  targetNeighbors.forEach((contact) => this.ongoingHandshakes.add(getNodeIdFromPeerDescriptor(contact.getPeerDescriptor())))
78
77
  return this.doParallelHandshakes(targetNeighbors, exclude)
79
78
  }
80
79
 
81
- private selectParallelTargets(excludedIds: NodeID[]): HandshakeRpcRemote[] {
80
+ private selectParallelTargets(excludedIds: DhtAddress[]): HandshakeRpcRemote[] {
82
81
  const targetNeighbors = this.config.nearbyNodeView.getClosestAndFurthest(excludedIds)
83
82
  while (targetNeighbors.length < PARALLEL_HANDSHAKE_COUNT && this.config.randomNodeView.size(excludedIds) > 0) {
84
83
  const random = this.config.randomNodeView.getRandom(excludedIds)
@@ -89,7 +88,7 @@ export class Handshaker {
89
88
  return targetNeighbors.map((neighbor) => this.createRpcRemote(neighbor.getPeerDescriptor()))
90
89
  }
91
90
 
92
- private async doParallelHandshakes(targets: HandshakeRpcRemote[], excludedIds: NodeID[]): Promise<NodeID[]> {
91
+ private async doParallelHandshakes(targets: HandshakeRpcRemote[], excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
93
92
  const results = await Promise.allSettled(
94
93
  Array.from(targets.values()).map(async (target: HandshakeRpcRemote, i) => {
95
94
  const otherNode = i === 0 ? targets[1] : targets[0]
@@ -98,7 +97,7 @@ export class Handshaker {
98
97
  return this.handshakeWithTarget(target, otherNodeId)
99
98
  })
100
99
  )
101
- results.map((res, i) => {
100
+ results.forEach((res, i) => {
102
101
  if (res.status !== 'fulfilled' || !res.value) {
103
102
  excludedIds.push(getNodeIdFromPeerDescriptor(targets[i].getPeerDescriptor()))
104
103
  }
@@ -106,7 +105,7 @@ export class Handshaker {
106
105
  return excludedIds
107
106
  }
108
107
 
109
- private async selectNewTargetAndHandshake(excludedIds: NodeID[]): Promise<NodeID[]> {
108
+ private async selectNewTargetAndHandshake(excludedIds: DhtAddress[]): Promise<DhtAddress[]> {
110
109
  const exclude = excludedIds.concat(this.config.targetNeighbors.getIds())
111
110
  const targetNeighbor = this.config.nearbyNodeView.getClosest(exclude) ?? this.config.randomNodeView.getRandom(exclude)
112
111
  if (targetNeighbor) {
@@ -118,10 +117,11 @@ export class Handshaker {
118
117
  return excludedIds
119
118
  }
120
119
 
121
- private async handshakeWithTarget(targetNeighbor: HandshakeRpcRemote, concurrentNodeId?: NodeID): Promise<boolean> {
120
+ private async handshakeWithTarget(targetNeighbor: HandshakeRpcRemote, concurrentNodeId?: DhtAddress): Promise<boolean> {
122
121
  const targetNodeId = getNodeIdFromPeerDescriptor(targetNeighbor.getPeerDescriptor())
123
122
  this.ongoingHandshakes.add(targetNodeId)
124
123
  const result = await targetNeighbor.handshake(
124
+ this.config.streamPartId,
125
125
  this.config.targetNeighbors.getIds(),
126
126
  concurrentNodeId
127
127
  )
@@ -136,11 +136,12 @@ export class Handshaker {
136
136
  return result.accepted
137
137
  }
138
138
 
139
- private async handshakeWithInterleaving(target: PeerDescriptor, interleaveSourceId: NodeID): Promise<boolean> {
139
+ private async handshakeWithInterleaving(target: PeerDescriptor, interleaveSourceId: DhtAddress): Promise<boolean> {
140
140
  const targetNeighbor = this.createRpcRemote(target)
141
141
  const targetNodeId = getNodeIdFromPeerDescriptor(targetNeighbor.getPeerDescriptor())
142
142
  this.ongoingHandshakes.add(targetNodeId)
143
143
  const result = await targetNeighbor.handshake(
144
+ this.config.streamPartId,
144
145
  this.config.targetNeighbors.getIds(),
145
146
  undefined,
146
147
  interleaveSourceId
@@ -157,7 +158,6 @@ export class Handshaker {
157
158
  return new HandshakeRpcRemote(
158
159
  this.config.localPeerDescriptor,
159
160
  targetPeerDescriptor,
160
- this.config.streamPartId,
161
161
  this.config.rpcCommunicator,
162
162
  HandshakeRpcClient,
163
163
  this.config.rpcRequestTimeout
@@ -168,14 +168,13 @@ export class Handshaker {
168
168
  return new DeliveryRpcRemote(
169
169
  this.config.localPeerDescriptor,
170
170
  targetPeerDescriptor,
171
- this.config.streamPartId,
172
171
  this.config.rpcCommunicator,
173
172
  DeliveryRpcClient,
174
173
  this.config.rpcRequestTimeout
175
174
  )
176
175
  }
177
176
 
178
- getOngoingHandshakes(): Set<NodeID> {
177
+ getOngoingHandshakes(): Set<DhtAddress> {
179
178
  return this.ongoingHandshakes
180
179
  }
181
180
 
@@ -1,11 +1,11 @@
1
1
  import { setAbortableTimeout } from '@streamr/utils'
2
2
  import { NodeList } from '../NodeList'
3
- import { NodeID } from '../../identifiers'
3
+ import { DhtAddress } from '@streamr/dht'
4
4
 
5
5
  interface FindNeighborsSessionConfig {
6
6
  targetNeighbors: NodeList
7
7
  nearbyNodeView: NodeList
8
- doFindNeighbors: (excludedNodes: NodeID[]) => Promise<NodeID[]>
8
+ doFindNeighbors: (excludedNodes: DhtAddress[]) => Promise<DhtAddress[]>
9
9
  minCount: number
10
10
  }
11
11
 
@@ -22,7 +22,7 @@ export class NeighborFinder {
22
22
  this.abortController = new AbortController()
23
23
  }
24
24
 
25
- private async findNeighbors(excluded: NodeID[]): Promise<void> {
25
+ private async findNeighbors(excluded: DhtAddress[]): Promise<void> {
26
26
  if (!this.running) {
27
27
  return
28
28
  }
@@ -39,7 +39,7 @@ export class NeighborFinder {
39
39
  return this.running
40
40
  }
41
41
 
42
- start(excluded: NodeID[] = []): void {
42
+ start(excluded: DhtAddress[] = []): void {
43
43
  if (this.running) {
44
44
  return
45
45
  }
@@ -1,12 +1,11 @@
1
1
  import { NeighborUpdate } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
2
- import { ListeningRpcCommunicator, PeerDescriptor } from '@streamr/dht'
2
+ import { ListeningRpcCommunicator, PeerDescriptor, getNodeIdFromPeerDescriptor } from '@streamr/dht'
3
3
  import { NeighborUpdateRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
4
4
  import { Logger, scheduleAtInterval } from '@streamr/utils'
5
5
  import { NeighborFinder } from './NeighborFinder'
6
6
  import { NodeList } from '../NodeList'
7
7
  import { NeighborUpdateRpcRemote } from './NeighborUpdateRpcRemote'
8
8
  import { NeighborUpdateRpcLocal } from './NeighborUpdateRpcLocal'
9
- import { getNodeIdFromPeerDescriptor } from '../../identifiers'
10
9
  import { StreamPartID } from '@streamr/protocol'
11
10
 
12
11
  interface NeighborUpdateManagerConfig {
@@ -47,7 +46,7 @@ export class NeighborUpdateManager {
47
46
  logger.trace(`Updating neighbor info to nodes`)
48
47
  const neighborDescriptors = this.config.targetNeighbors.getAll().map((neighbor) => neighbor.getPeerDescriptor())
49
48
  await Promise.allSettled(this.config.targetNeighbors.getAll().map(async (neighbor) => {
50
- const res = await this.createRemote(neighbor.getPeerDescriptor()).updateNeighbors(neighborDescriptors)
49
+ const res = await this.createRemote(neighbor.getPeerDescriptor()).updateNeighbors(this.config.streamPartId, neighborDescriptors)
51
50
  if (res.removeMe) {
52
51
  this.config.targetNeighbors.remove(neighbor.getPeerDescriptor())
53
52
  this.config.neighborFinder.start([getNodeIdFromPeerDescriptor(neighbor.getPeerDescriptor())])
@@ -59,7 +58,6 @@ export class NeighborUpdateManager {
59
58
  return new NeighborUpdateRpcRemote(
60
59
  this.config.localPeerDescriptor,
61
60
  targetPeerDescriptor,
62
- this.config.streamPartId,
63
61
  this.config.rpcCommunicator,
64
62
  NeighborUpdateRpcClient
65
63
  )
@@ -1,6 +1,5 @@
1
1
  import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
2
- import { DhtCallContext, ListeningRpcCommunicator, PeerDescriptor } from '@streamr/dht'
3
- import { getNodeIdFromPeerDescriptor } from '../../identifiers'
2
+ import { DhtCallContext, ListeningRpcCommunicator, PeerDescriptor, getNodeIdFromPeerDescriptor } from '@streamr/dht'
4
3
  import { NeighborUpdate } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
5
4
  import { DeliveryRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
6
5
  import { INeighborUpdateRpc } from '../../proto/packages/trackerless-network/protos/NetworkRpc.server'
@@ -31,17 +30,15 @@ export class NeighborUpdateRpcLocal implements INeighborUpdateRpc {
31
30
  const senderPeerDescriptor = (context as DhtCallContext).incomingSourceDescriptor!
32
31
  const senderId = getNodeIdFromPeerDescriptor(senderPeerDescriptor)
33
32
  if (this.config.targetNeighbors.hasNodeById(senderId)) {
34
- const newPeerDescriptors = message.neighborDescriptors
35
- .filter((peerDescriptor) => {
36
- const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
37
- const ownNodeId = getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
38
- return nodeId !== ownNodeId && !this.config.targetNeighbors.getIds().includes(nodeId)
39
- })
33
+ const ownNodeId = getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
34
+ const newPeerDescriptors = message.neighborDescriptors.filter((peerDescriptor) => {
35
+ const nodeId = getNodeIdFromPeerDescriptor(peerDescriptor)
36
+ return nodeId !== ownNodeId && !this.config.targetNeighbors.getIds().includes(nodeId)
37
+ })
40
38
  newPeerDescriptors.forEach((peerDescriptor) => this.config.nearbyNodeView.add(
41
39
  new DeliveryRpcRemote(
42
40
  this.config.localPeerDescriptor,
43
41
  peerDescriptor,
44
- this.config.streamPartId,
45
42
  this.config.rpcCommunicator,
46
43
  DeliveryRpcClient
47
44
  ))
@@ -1,8 +1,8 @@
1
- import { PeerDescriptor, RpcRemote } from '@streamr/dht'
1
+ import { PeerDescriptor, RpcRemote, getNodeIdFromPeerDescriptor } from '@streamr/dht'
2
2
  import { Logger } from '@streamr/utils'
3
- import { getNodeIdFromPeerDescriptor } from '../../identifiers'
4
3
  import { NeighborUpdate } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
5
4
  import { NeighborUpdateRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
5
+ import { StreamPartID } from '@streamr/protocol'
6
6
 
7
7
  const logger = new Logger(module)
8
8
 
@@ -13,9 +13,9 @@ interface UpdateNeighborsResponse {
13
13
 
14
14
  export class NeighborUpdateRpcRemote extends RpcRemote<NeighborUpdateRpcClient> {
15
15
 
16
- async updateNeighbors(neighbors: PeerDescriptor[]): Promise<UpdateNeighborsResponse> {
16
+ async updateNeighbors(streamPartId: StreamPartID, neighbors: PeerDescriptor[]): Promise<UpdateNeighborsResponse> {
17
17
  const request: NeighborUpdate = {
18
- streamPartId: this.getServiceId(),
18
+ streamPartId,
19
19
  neighborDescriptors: neighbors,
20
20
  removeMe: false
21
21
  }
@@ -1,8 +1,8 @@
1
- import { NodeID } from '../../identifiers'
1
+ import { DhtAddress } from '@streamr/dht'
2
2
  import { StreamMessage } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
3
3
  import { PropagationTask, PropagationTaskStore } from './PropagationTaskStore'
4
4
 
5
- type SendToNeighborFn = (neighborId: NodeID, msg: StreamMessage) => Promise<void>
5
+ type SendToNeighborFn = (neighborId: DhtAddress, msg: StreamMessage) => Promise<void>
6
6
 
7
7
  interface ConstructorOptions {
8
8
  sendToNeighbor: SendToNeighborFn
@@ -41,11 +41,11 @@ export class Propagation {
41
41
  /**
42
42
  * Node should invoke this when it learns about a new message
43
43
  */
44
- feedUnseenMessage(message: StreamMessage, targets: NodeID[], source: NodeID | null): void {
44
+ feedUnseenMessage(message: StreamMessage, targets: DhtAddress[], source: DhtAddress | null): void {
45
45
  const task = {
46
46
  message,
47
47
  source,
48
- handledNeighbors: new Set<NodeID>()
48
+ handledNeighbors: new Set<DhtAddress>()
49
49
  }
50
50
  this.activeTaskStore.add(task)
51
51
  for (const target of targets) {
@@ -56,14 +56,14 @@ export class Propagation {
56
56
  /**
57
57
  * Node should invoke this when it learns about a new node stream assignment
58
58
  */
59
- onNeighborJoined(neighborId: NodeID): void {
59
+ onNeighborJoined(neighborId: DhtAddress): void {
60
60
  const tasks = this.activeTaskStore.get()
61
61
  for (const task of tasks) {
62
62
  this.sendAndAwaitThenMark(task, neighborId)
63
63
  }
64
64
  }
65
65
 
66
- private sendAndAwaitThenMark({ message, source, handledNeighbors }: PropagationTask, neighborId: NodeID): void {
66
+ private sendAndAwaitThenMark({ message, source, handledNeighbors }: PropagationTask, neighborId: DhtAddress): void {
67
67
  if (!handledNeighbors.has(neighborId) && neighborId !== source) {
68
68
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
69
69
  (async () => {
@@ -1,11 +1,11 @@
1
- import { NodeID } from '../../identifiers'
1
+ import { DhtAddress } from '@streamr/dht'
2
2
  import { MessageRef, StreamMessage } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
3
3
  import { FifoMapWithTTL } from './FifoMapWithTTL'
4
4
 
5
5
  export interface PropagationTask {
6
6
  message: StreamMessage
7
7
  source: string | null
8
- handledNeighbors: Set<NodeID>
8
+ handledNeighbors: Set<DhtAddress>
9
9
  }
10
10
 
11
11
  /**
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  ConnectionLocker,
3
+ DhtAddress,
3
4
  ITransport,
4
5
  ListeningRpcCommunicator,
5
- PeerDescriptor
6
+ PeerDescriptor,
7
+ getNodeIdFromPeerDescriptor
6
8
  } from '@streamr/dht'
7
9
  import { StreamPartID } from '@streamr/protocol'
8
10
  import { EthereumAddress, Logger, addManagedEventListener, wait } from '@streamr/utils'
9
11
  import { EventEmitter } from 'eventemitter3'
10
12
  import { sampleSize } from 'lodash'
11
- import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
12
13
  import {
13
14
  LeaveStreamPartNotice,
14
15
  MessageID,
@@ -51,7 +52,7 @@ interface ProxyClientConfig {
51
52
  }
52
53
 
53
54
  interface ProxyDefinition {
54
- nodes: Map<NodeID, PeerDescriptor>
55
+ nodes: Map<DhtAddress, PeerDescriptor>
55
56
  connectionCount: number
56
57
  direction: ProxyDirection
57
58
  userId: EthereumAddress
@@ -72,7 +73,7 @@ export class ProxyClient extends EventEmitter<Events> {
72
73
  private readonly config: ProxyClientConfig
73
74
  private readonly duplicateDetectors: Map<string, DuplicateMessageDetector> = new Map()
74
75
  private definition?: ProxyDefinition
75
- private readonly connections: Map<NodeID, ProxyDirection> = new Map()
76
+ private readonly connections: Map<DhtAddress, ProxyDirection> = new Map()
76
77
  private readonly propagation: Propagation
77
78
  private readonly targetNeighbors: NodeList
78
79
  private readonly abortController: AbortController
@@ -87,8 +88,8 @@ export class ProxyClient extends EventEmitter<Events> {
87
88
  localPeerDescriptor: this.config.localPeerDescriptor,
88
89
  streamPartId: this.config.streamPartId,
89
90
  markAndCheckDuplicate: (msg: MessageID, prev?: MessageRef) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
90
- broadcast: (message: StreamMessage, previousNode?: NodeID) => this.broadcast(message, previousNode),
91
- onLeaveNotice: (senderId: NodeID) => {
91
+ broadcast: (message: StreamMessage, previousNode?: DhtAddress) => this.broadcast(message, previousNode),
92
+ onLeaveNotice: (senderId: DhtAddress) => {
92
93
  const contact = this.targetNeighbors.get(senderId)
93
94
  if (contact) {
94
95
  // TODO should we catch possible promise rejection?
@@ -101,7 +102,7 @@ export class ProxyClient extends EventEmitter<Events> {
101
102
  this.propagation = new Propagation({
102
103
  // TODO use config option or named constant?
103
104
  minPropagationTargets: config.minPropagationTargets ?? 2,
104
- sendToNeighbor: async (neighborId: NodeID, msg: StreamMessage): Promise<void> => {
105
+ sendToNeighbor: async (neighborId: DhtAddress, msg: StreamMessage): Promise<void> => {
105
106
  const remote = this.targetNeighbors.get(neighborId)
106
107
  if (remote) {
107
108
  await remote.sendStreamMessage(msg)
@@ -130,7 +131,7 @@ export class ProxyClient extends EventEmitter<Events> {
130
131
  if (connectionCount !== undefined && connectionCount > nodes.length) {
131
132
  throw new Error('Cannot set connectionCount above the size of the configured array of nodes')
132
133
  }
133
- const nodesIds = new Map<NodeID, PeerDescriptor>()
134
+ const nodesIds = new Map<DhtAddress, PeerDescriptor>()
134
135
  nodes.forEach((peerDescriptor) => {
135
136
  nodesIds.set(getNodeIdFromPeerDescriptor(peerDescriptor), peerDescriptor)
136
137
  })
@@ -155,7 +156,7 @@ export class ProxyClient extends EventEmitter<Events> {
155
156
  }
156
157
  }
157
158
 
158
- private getInvalidConnections(): NodeID[] {
159
+ private getInvalidConnections(): DhtAddress[] {
159
160
  return Array.from(this.connections.keys()).filter((id) => {
160
161
  return !this.definition!.nodes.has(id)
161
162
  || this.definition!.direction !== this.connections.get(id)
@@ -164,19 +165,18 @@ export class ProxyClient extends EventEmitter<Events> {
164
165
 
165
166
  private async openRandomConnections(connectionCount: number): Promise<void> {
166
167
  const proxiesToAttempt = sampleSize(Array.from(this.definition!.nodes.keys()).filter((id) =>
167
- !this.connections.has(id as unknown as NodeID)
168
+ !this.connections.has(id)
168
169
  ), connectionCount)
169
170
  await Promise.all(proxiesToAttempt.map((id) =>
170
- this.attemptConnection(id as unknown as NodeID, this.definition!.direction, this.definition!.userId)
171
+ this.attemptConnection(id, this.definition!.direction, this.definition!.userId)
171
172
  ))
172
173
  }
173
174
 
174
- private async attemptConnection(nodeId: NodeID, direction: ProxyDirection, userId: EthereumAddress): Promise<void> {
175
+ private async attemptConnection(nodeId: DhtAddress, direction: ProxyDirection, userId: EthereumAddress): Promise<void> {
175
176
  const peerDescriptor = this.definition!.nodes.get(nodeId)!
176
177
  const rpcRemote = new ProxyConnectionRpcRemote(
177
178
  this.config.localPeerDescriptor,
178
179
  peerDescriptor,
179
- this.config.streamPartId,
180
180
  this.rpcCommunicator,
181
181
  ProxyConnectionRpcClient
182
182
  )
@@ -187,7 +187,6 @@ export class ProxyClient extends EventEmitter<Events> {
187
187
  const remote = new DeliveryRpcRemote(
188
188
  this.config.localPeerDescriptor,
189
189
  peerDescriptor,
190
- this.config.streamPartId,
191
190
  this.rpcCommunicator,
192
191
  DeliveryRpcClient
193
192
  )
@@ -210,23 +209,23 @@ export class ProxyClient extends EventEmitter<Events> {
210
209
  await Promise.allSettled(proxiesToDisconnect.map((node) => this.closeConnection(node)))
211
210
  }
212
211
 
213
- private async closeConnection(nodeId: NodeID): Promise<void> {
212
+ private async closeConnection(nodeId: DhtAddress): Promise<void> {
214
213
  if (this.connections.has(nodeId)) {
215
214
  logger.info('Close proxy connection', {
216
215
  nodeId
217
216
  })
218
217
  const server = this.targetNeighbors.get(nodeId)
219
- server?.leaveStreamPartNotice(false)
218
+ server?.leaveStreamPartNotice(this.config.streamPartId, false)
220
219
  this.removeConnection(nodeId)
221
220
  }
222
221
  }
223
222
 
224
- private removeConnection(nodeId: NodeID): void {
223
+ private removeConnection(nodeId: DhtAddress): void {
225
224
  this.connections.delete(nodeId)
226
225
  this.targetNeighbors.removeById(nodeId)
227
226
  }
228
227
 
229
- broadcast(msg: StreamMessage, previousNode?: NodeID): void {
228
+ broadcast(msg: StreamMessage, previousNode?: DhtAddress): void {
230
229
  if (!previousNode) {
231
230
  markAndCheckDuplicate(this.duplicateDetectors, msg.messageId!, msg.previousMessageRef)
232
231
  }
@@ -234,7 +233,7 @@ export class ProxyClient extends EventEmitter<Events> {
234
233
  this.propagation.feedUnseenMessage(msg, this.targetNeighbors.getIds(), previousNode ?? null)
235
234
  }
236
235
 
237
- hasConnection(nodeId: NodeID, direction: ProxyDirection): boolean {
236
+ hasConnection(nodeId: DhtAddress, direction: ProxyDirection): boolean {
238
237
  return this.connections.has(nodeId) && this.connections.get(nodeId) === direction
239
238
  }
240
239
 
@@ -263,9 +262,9 @@ export class ProxyClient extends EventEmitter<Events> {
263
262
  }
264
263
 
265
264
  stop(): void {
266
- this.targetNeighbors.getAll().map((remote) => {
265
+ this.targetNeighbors.getAll().forEach((remote) => {
267
266
  this.config.connectionLocker.unlockConnection(remote.getPeerDescriptor(), SERVICE_ID)
268
- remote.leaveStreamPartNotice(false)
267
+ remote.leaveStreamPartNotice(this.config.streamPartId, false)
269
268
  })
270
269
  this.targetNeighbors.stop()
271
270
  this.rpcCommunicator.destroy()
@@ -9,12 +9,11 @@ import {
9
9
  } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
10
10
  import { IProxyConnectionRpc } from '../../proto/packages/trackerless-network/protos/NetworkRpc.server'
11
11
  import { DeliveryRpcRemote } from '../DeliveryRpcRemote'
12
- import { DhtCallContext, ListeningRpcCommunicator, PeerDescriptor } from '@streamr/dht'
12
+ import { DhtAddress, DhtCallContext, ListeningRpcCommunicator, PeerDescriptor, getNodeIdFromPeerDescriptor } from '@streamr/dht'
13
13
  import { DeliveryRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
14
14
  import { EventEmitter } from 'eventemitter3'
15
15
  import { EthereumAddress, Logger, binaryToHex, toEthereumAddress } from '@streamr/utils'
16
16
  import { StreamPartID } from '@streamr/protocol'
17
- import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
18
17
 
19
18
  const logger = new Logger(module)
20
19
 
@@ -31,13 +30,13 @@ interface ProxyConnectionRpcLocalConfig {
31
30
  }
32
31
 
33
32
  export interface Events {
34
- newConnection: (nodeId: NodeID) => void
33
+ newConnection: (nodeId: DhtAddress) => void
35
34
  }
36
35
 
37
36
  export class ProxyConnectionRpcLocal extends EventEmitter<Events> implements IProxyConnectionRpc {
38
37
 
39
38
  private readonly config: ProxyConnectionRpcLocalConfig
40
- private readonly connections: Map<NodeID, ProxyConnection> = new Map()
39
+ private readonly connections: Map<DhtAddress, ProxyConnection> = new Map()
41
40
 
42
41
  constructor(config: ProxyConnectionRpcLocalConfig) {
43
42
  super()
@@ -46,25 +45,25 @@ export class ProxyConnectionRpcLocal extends EventEmitter<Events> implements IPr
46
45
  (msg: ProxyConnectionRequest, context) => this.requestConnection(msg, context))
47
46
  }
48
47
 
49
- getConnection(nodeId: NodeID): ProxyConnection | undefined {
48
+ getConnection(nodeId: DhtAddress): ProxyConnection | undefined {
50
49
  return this.connections.get(nodeId)
51
50
  }
52
51
 
53
- hasConnection(nodeId: NodeID): boolean {
52
+ hasConnection(nodeId: DhtAddress): boolean {
54
53
  return this.connections.has(nodeId)
55
54
  }
56
55
 
57
- removeConnection(nodeId: NodeID): void {
56
+ removeConnection(nodeId: DhtAddress): void {
58
57
  this.connections.delete(nodeId)
59
58
  }
60
59
 
61
60
  stop(): void {
62
- this.connections.forEach((connection) => connection.remote.leaveStreamPartNotice(false))
61
+ this.connections.forEach((connection) => connection.remote.leaveStreamPartNotice(this.config.streamPartId, false))
63
62
  this.connections.clear()
64
63
  this.removeAllListeners()
65
64
  }
66
65
 
67
- getPropagationTargets(msg: StreamMessage): NodeID[] {
66
+ getPropagationTargets(msg: StreamMessage): DhtAddress[] {
68
67
  if (msg.messageType === StreamMessageType.GROUP_KEY_REQUEST) {
69
68
  try {
70
69
  const recipientId = GroupKeyRequest.fromBinary(msg.content).recipientId
@@ -78,11 +77,11 @@ export class ProxyConnectionRpcLocal extends EventEmitter<Events> implements IPr
78
77
  }
79
78
  }
80
79
 
81
- private getNodeIdsForUserId(userId: EthereumAddress): NodeID[] {
80
+ private getNodeIdsForUserId(userId: EthereumAddress): DhtAddress[] {
82
81
  return Array.from(this.connections.keys()).filter((nodeId) => this.connections.get(nodeId)!.userId === userId)
83
82
  }
84
83
 
85
- private getSubscribers(): NodeID[] {
84
+ private getSubscribers(): DhtAddress[] {
86
85
  return Array.from(this.connections.keys()).filter((key) => this.connections.get(key)!.direction === ProxyDirection.SUBSCRIBE)
87
86
  }
88
87
 
@@ -96,7 +95,6 @@ export class ProxyConnectionRpcLocal extends EventEmitter<Events> implements IPr
96
95
  remote: new DeliveryRpcRemote(
97
96
  this.config.localPeerDescriptor,
98
97
  senderPeerDescriptor,
99
- this.config.streamPartId,
100
98
  this.config.rpcCommunicator,
101
99
  DeliveryRpcClient
102
100
  )