@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.
- package/dist/package.json +6 -6
- package/dist/src/NetworkNode.d.ts +3 -4
- package/dist/src/NetworkNode.js.map +1 -1
- package/dist/src/NetworkStack.js +1 -1
- package/dist/src/NetworkStack.js.map +1 -1
- package/dist/src/exports.d.ts +0 -1
- package/dist/src/exports.js.map +1 -1
- package/dist/src/logic/DeliveryRpcLocal.d.ts +4 -5
- package/dist/src/logic/DeliveryRpcLocal.js +3 -3
- package/dist/src/logic/DeliveryRpcLocal.js.map +1 -1
- package/dist/src/logic/DeliveryRpcRemote.d.ts +2 -1
- package/dist/src/logic/DeliveryRpcRemote.js +2 -2
- package/dist/src/logic/DeliveryRpcRemote.js.map +1 -1
- package/dist/src/logic/EntryPointDiscovery.d.ts +5 -5
- package/dist/src/logic/EntryPointDiscovery.js +4 -5
- package/dist/src/logic/EntryPointDiscovery.js.map +1 -1
- package/dist/src/logic/Layer0Node.d.ts +4 -4
- package/dist/src/logic/NodeList.d.ts +12 -13
- package/dist/src/logic/NodeList.js +4 -4
- package/dist/src/logic/NodeList.js.map +1 -1
- package/dist/src/logic/RandomGraphNode.d.ts +6 -7
- package/dist/src/logic/RandomGraphNode.js +9 -9
- package/dist/src/logic/RandomGraphNode.js.map +1 -1
- package/dist/src/logic/StreamrNode.d.ts +3 -4
- package/dist/src/logic/StreamrNode.js +4 -4
- package/dist/src/logic/StreamrNode.js.map +1 -1
- package/dist/src/logic/createRandomGraphNode.js +1 -3
- package/dist/src/logic/createRandomGraphNode.js.map +1 -1
- package/dist/src/logic/inspect/InspectSession.d.ts +3 -3
- package/dist/src/logic/inspect/InspectSession.js.map +1 -1
- package/dist/src/logic/inspect/Inspector.d.ts +3 -4
- package/dist/src/logic/inspect/Inspector.js +3 -3
- package/dist/src/logic/inspect/Inspector.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.d.ts +4 -5
- package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js +11 -10
- package/dist/src/logic/neighbor-discovery/HandshakeRpcLocal.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.d.ts +3 -3
- package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js +7 -8
- package/dist/src/logic/neighbor-discovery/HandshakeRpcRemote.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/Handshaker.d.ts +3 -4
- package/dist/src/logic/neighbor-discovery/Handshaker.js +12 -12
- package/dist/src/logic/neighbor-discovery/Handshaker.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborFinder.d.ts +3 -3
- package/dist/src/logic/neighbor-discovery/NeighborFinder.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js +4 -4
- package/dist/src/logic/neighbor-discovery/NeighborUpdateManager.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js +6 -7
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.js.map +1 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.d.ts +2 -1
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js +3 -4
- package/dist/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.js.map +1 -1
- package/dist/src/logic/propagation/Propagation.d.ts +4 -4
- package/dist/src/logic/propagation/Propagation.js.map +1 -1
- package/dist/src/logic/propagation/PropagationTaskStore.d.ts +2 -2
- package/dist/src/logic/proxy/ProxyClient.d.ts +3 -4
- package/dist/src/logic/proxy/ProxyClient.js +8 -9
- package/dist/src/logic/proxy/ProxyClient.js.map +1 -1
- package/dist/src/logic/proxy/ProxyConnectionRpcLocal.d.ts +6 -7
- package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js +4 -4
- package/dist/src/logic/proxy/ProxyConnectionRpcLocal.js.map +1 -1
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.d.ts +0 -2
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js +3 -3
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcLocal.js.map +1 -1
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js +1 -2
- package/dist/src/logic/temporary-connection/TemporaryConnectionRpcRemote.js.map +1 -1
- package/dist/test/benchmark/first-message.js +2 -3
- package/dist/test/benchmark/first-message.js.map +1 -1
- package/dist/test/utils/utils.d.ts +2 -3
- package/dist/test/utils/utils.js +4 -4
- package/dist/test/utils/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/NetworkNode.ts +3 -4
- package/src/NetworkStack.ts +1 -1
- package/src/exports.ts +0 -1
- package/src/logic/DeliveryRpcLocal.ts +4 -5
- package/src/logic/DeliveryRpcRemote.ts +3 -2
- package/src/logic/EntryPointDiscovery.ts +11 -9
- package/src/logic/Layer0Node.ts +4 -4
- package/src/logic/NodeList.ts +15 -16
- package/src/logic/RandomGraphNode.ts +16 -17
- package/src/logic/StreamrNode.ts +8 -6
- package/src/logic/createRandomGraphNode.ts +2 -4
- package/src/logic/inspect/InspectSession.ts +4 -4
- package/src/logic/inspect/Inspector.ts +4 -6
- package/src/logic/neighbor-discovery/HandshakeRpcLocal.ts +20 -12
- package/src/logic/neighbor-discovery/HandshakeRpcRemote.ts +11 -10
- package/src/logic/neighbor-discovery/Handshaker.ts +14 -15
- package/src/logic/neighbor-discovery/NeighborFinder.ts +4 -4
- package/src/logic/neighbor-discovery/NeighborUpdateManager.ts +2 -4
- package/src/logic/neighbor-discovery/NeighborUpdateRpcLocal.ts +6 -9
- package/src/logic/neighbor-discovery/NeighborUpdateRpcRemote.ts +4 -4
- package/src/logic/propagation/Propagation.ts +6 -6
- package/src/logic/propagation/PropagationTaskStore.ts +2 -2
- package/src/logic/proxy/ProxyClient.ts +20 -21
- package/src/logic/proxy/ProxyConnectionRpcLocal.ts +10 -12
- package/src/logic/temporary-connection/TemporaryConnectionRpcLocal.ts +1 -5
- package/src/logic/temporary-connection/TemporaryConnectionRpcRemote.ts +1 -2
- package/test/benchmark/first-message.ts +2 -3
- package/test/end-to-end/proxy-connections.test.ts +2 -2
- package/test/end-to-end/webrtc-full-node-network.test.ts +1 -2
- package/test/end-to-end/websocket-full-node-network.test.ts +1 -1
- package/test/integration/DeliveryRpcRemote.test.ts +2 -5
- package/test/integration/HandshakeRpcRemote.test.ts +2 -2
- package/test/integration/Handshakes.test.ts +2 -6
- package/test/integration/NeighborUpdateRpcRemote.test.ts +1 -2
- package/test/unit/HandshakeRpcLocal.test.ts +9 -11
- package/test/unit/Handshaker.test.ts +1 -2
- package/test/unit/InspectSession.test.ts +3 -3
- package/test/unit/Inspector.test.ts +1 -2
- package/test/unit/NeighborFinder.test.ts +2 -2
- package/test/unit/NodeList.test.ts +12 -13
- package/test/unit/Propagation.test.ts +9 -9
- package/test/unit/ProxyConnectionRpcRemote.test.ts +0 -2
- package/test/unit/RandomGraphNode.test.ts +1 -1
- package/test/utils/mock/MockHandshaker.ts +3 -3
- package/test/utils/utils.ts +15 -7
- package/dist/src/identifiers.d.ts +0 -4
- package/dist/src/identifiers.js +0 -9
- package/dist/src/identifiers.js.map +0 -1
- package/src/identifiers.ts +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamr/trackerless-network",
|
|
3
|
-
"version": "100.0.0-testnet-two.
|
|
3
|
+
"version": "100.0.0-testnet-two.3",
|
|
4
4
|
"description": "Minimal and extendable implementation of the Streamr Network node.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@protobuf-ts/runtime": "^2.8.2",
|
|
32
32
|
"@protobuf-ts/runtime-rpc": "^2.8.2",
|
|
33
|
-
"@streamr/dht": "100.0.0-testnet-two.
|
|
34
|
-
"@streamr/proto-rpc": "100.0.0-testnet-two.
|
|
35
|
-
"@streamr/protocol": "100.0.0-testnet-two.
|
|
36
|
-
"@streamr/test-utils": "100.0.0-testnet-two.
|
|
37
|
-
"@streamr/utils": "100.0.0-testnet-two.
|
|
33
|
+
"@streamr/dht": "100.0.0-testnet-two.3",
|
|
34
|
+
"@streamr/proto-rpc": "100.0.0-testnet-two.3",
|
|
35
|
+
"@streamr/protocol": "100.0.0-testnet-two.3",
|
|
36
|
+
"@streamr/test-utils": "100.0.0-testnet-two.3",
|
|
37
|
+
"@streamr/utils": "100.0.0-testnet-two.3",
|
|
38
38
|
"eventemitter3": "^5.0.0",
|
|
39
39
|
"lodash": "^4.17.21",
|
|
40
40
|
"uuid": "^9.0.1",
|
package/src/NetworkNode.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { StreamMessage, StreamPartID } from '@streamr/protocol'
|
|
2
|
-
import { PeerDescriptor } from '@streamr/dht'
|
|
2
|
+
import { DhtAddress, PeerDescriptor } from '@streamr/dht'
|
|
3
3
|
import { StreamMessageTranslator } from './logic/protocol-integration/stream-message/StreamMessageTranslator'
|
|
4
4
|
import { NetworkOptions, NetworkStack } from './NetworkStack'
|
|
5
5
|
import { EthereumAddress, Logger, MetricsContext } from '@streamr/utils'
|
|
6
6
|
import { ProxyDirection } from './proto/packages/trackerless-network/protos/NetworkRpc'
|
|
7
|
-
import { NodeID } from './identifiers'
|
|
8
7
|
import { pull } from 'lodash'
|
|
9
8
|
|
|
10
9
|
export const createNetworkNode = (opts: NetworkOptions): NetworkNode => {
|
|
@@ -88,7 +87,7 @@ export class NetworkNode {
|
|
|
88
87
|
await this.stack.getStreamrNode().leaveStreamPart(streamPartId)
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
getNeighbors(streamPartId: StreamPartID): ReadonlyArray<
|
|
90
|
+
getNeighbors(streamPartId: StreamPartID): ReadonlyArray<DhtAddress> {
|
|
92
91
|
return this.stack.getStreamrNode().getNeighbors(streamPartId)
|
|
93
92
|
}
|
|
94
93
|
|
|
@@ -109,7 +108,7 @@ export class NetworkNode {
|
|
|
109
108
|
return this.stack.getMetricsContext()
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
getNodeId():
|
|
111
|
+
getNodeId(): DhtAddress {
|
|
113
112
|
return this.stack.getStreamrNode().getNodeId()
|
|
114
113
|
}
|
|
115
114
|
|
package/src/NetworkStack.ts
CHANGED
|
@@ -107,8 +107,8 @@ export class NetworkStack {
|
|
|
107
107
|
await this.layer0Node?.joinDht(this.options.layer0.entryPoints)
|
|
108
108
|
}
|
|
109
109
|
})
|
|
110
|
+
await this.layer0Node!.waitForNetworkConnectivity()
|
|
110
111
|
}
|
|
111
|
-
await this.layer0Node!.waitForNetworkConnectivity()
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
getStreamrNode(): StreamrNode {
|
package/src/exports.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { NetworkStack, NetworkOptions } from './NetworkStack'
|
|
2
2
|
export { NetworkNode, createNetworkNode } from './NetworkNode'
|
|
3
3
|
export { StreamrNodeConfig } from './logic/StreamrNode'
|
|
4
|
-
export { NodeID } from './identifiers'
|
|
5
4
|
export { ProxyDirection } from './proto/packages/trackerless-network/protos/NetworkRpc'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ListeningRpcCommunicator, PeerDescriptor, DhtCallContext } from '@streamr/dht'
|
|
1
|
+
import { ListeningRpcCommunicator, PeerDescriptor, DhtCallContext, DhtAddress, getNodeIdFromPeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { Empty } from '../proto/google/protobuf/empty'
|
|
3
3
|
import {
|
|
4
4
|
LeaveStreamPartNotice,
|
|
@@ -8,16 +8,15 @@ import {
|
|
|
8
8
|
} from '../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
9
9
|
import { IDeliveryRpc } from '../proto/packages/trackerless-network/protos/NetworkRpc.server'
|
|
10
10
|
import { ServerCallContext } from '@protobuf-ts/runtime-rpc'
|
|
11
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
12
11
|
import { StreamPartID } from '@streamr/protocol'
|
|
13
12
|
|
|
14
13
|
export interface DeliveryRpcLocalConfig {
|
|
15
14
|
localPeerDescriptor: PeerDescriptor
|
|
16
15
|
streamPartId: StreamPartID
|
|
17
16
|
markAndCheckDuplicate: (messageId: MessageID, previousMessageRef?: MessageRef) => boolean
|
|
18
|
-
broadcast: (message: StreamMessage, previousNode?:
|
|
19
|
-
onLeaveNotice(senderId:
|
|
20
|
-
markForInspection(senderId:
|
|
17
|
+
broadcast: (message: StreamMessage, previousNode?: DhtAddress) => void
|
|
18
|
+
onLeaveNotice(senderId: DhtAddress, isLocalNodeEntryPoint: boolean): void
|
|
19
|
+
markForInspection(senderId: DhtAddress, messageId: MessageID): void
|
|
21
20
|
rpcCommunicator: ListeningRpcCommunicator
|
|
22
21
|
}
|
|
23
22
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
StreamMessage
|
|
6
6
|
} from '../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
7
7
|
import { DeliveryRpcClient } from '../proto/packages/trackerless-network/protos/NetworkRpc.client'
|
|
8
|
+
import { StreamPartID } from '@streamr/protocol'
|
|
8
9
|
|
|
9
10
|
const logger = new Logger(module)
|
|
10
11
|
|
|
@@ -19,9 +20,9 @@ export class DeliveryRpcRemote extends RpcRemote<DeliveryRpcClient> {
|
|
|
19
20
|
})
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
leaveStreamPartNotice(isLocalNodeEntryPoint: boolean): void {
|
|
23
|
+
leaveStreamPartNotice(streamPartId: StreamPartID, isLocalNodeEntryPoint: boolean): void {
|
|
23
24
|
const notification: LeaveStreamPartNotice = {
|
|
24
|
-
streamPartId
|
|
25
|
+
streamPartId,
|
|
25
26
|
isEntryPoint: isLocalNodeEntryPoint
|
|
26
27
|
}
|
|
27
28
|
const options = this.formDhtRpcOptions({
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataEntry,
|
|
3
|
+
DhtAddress,
|
|
3
4
|
PeerDescriptor,
|
|
4
|
-
areEqualPeerDescriptors
|
|
5
|
+
areEqualPeerDescriptors,
|
|
6
|
+
getDhtAddressFromRaw,
|
|
7
|
+
getNodeIdFromPeerDescriptor
|
|
5
8
|
} from '@streamr/dht'
|
|
6
9
|
import { StreamPartID } from '@streamr/protocol'
|
|
7
10
|
import { Logger, scheduleAtInterval, wait } from '@streamr/utils'
|
|
8
11
|
import { createHash } from 'crypto'
|
|
9
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
10
12
|
import { Any } from '../proto/google/protobuf/any'
|
|
11
13
|
import { Layer1Node } from './Layer1Node'
|
|
12
14
|
|
|
13
|
-
export const streamPartIdToDataKey = (streamPartId: StreamPartID):
|
|
14
|
-
return new Uint8Array(createHash('md5').update(streamPartId).digest())
|
|
15
|
+
export const streamPartIdToDataKey = (streamPartId: StreamPartID): DhtAddress => {
|
|
16
|
+
return getDhtAddressFromRaw(new Uint8Array(createHash('md5').update(streamPartId).digest()))
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
const parseEntryPointData = (dataEntries: DataEntry[]): PeerDescriptor[] => {
|
|
@@ -58,9 +60,9 @@ interface EntryPointDiscoveryConfig {
|
|
|
58
60
|
streamPartId: StreamPartID
|
|
59
61
|
localPeerDescriptor: PeerDescriptor
|
|
60
62
|
layer1Node: Layer1Node
|
|
61
|
-
getEntryPointData: (key:
|
|
62
|
-
storeEntryPointData: (key:
|
|
63
|
-
deleteEntryPointData: (key:
|
|
63
|
+
getEntryPointData: (key: DhtAddress) => Promise<DataEntry[]>
|
|
64
|
+
storeEntryPointData: (key: DhtAddress, data: Any) => Promise<PeerDescriptor[]>
|
|
65
|
+
deleteEntryPointData: (key: DhtAddress) => Promise<void>
|
|
64
66
|
storeInterval?: number
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -68,7 +70,7 @@ export class EntryPointDiscovery {
|
|
|
68
70
|
private readonly abortController: AbortController
|
|
69
71
|
private readonly config: EntryPointDiscoveryConfig
|
|
70
72
|
private readonly storeInterval: number
|
|
71
|
-
private readonly networkSplitAvoidedNodes: Set<
|
|
73
|
+
private readonly networkSplitAvoidedNodes: Set<DhtAddress> = new Set()
|
|
72
74
|
private isLocalNodeStoredAsEntryPoint = false
|
|
73
75
|
constructor(config: EntryPointDiscoveryConfig) {
|
|
74
76
|
this.config = config
|
|
@@ -106,7 +108,7 @@ export class EntryPointDiscovery {
|
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
private async queryEntrypoints(key:
|
|
111
|
+
private async queryEntrypoints(key: DhtAddress): Promise<PeerDescriptor[]> {
|
|
110
112
|
logger.trace(`Finding data from dht node ${getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)}`)
|
|
111
113
|
try {
|
|
112
114
|
const result = await this.config.getEntryPointData(key)
|
package/src/logic/Layer0Node.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { DataEntry, ITransport, PeerDescriptor } from '@streamr/dht'
|
|
1
|
+
import { DataEntry, DhtAddress, ITransport, PeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { Any } from '../proto/google/protobuf/any'
|
|
3
3
|
|
|
4
4
|
export interface Layer0Node extends ITransport {
|
|
5
5
|
joinDht(entryPointDescriptors: PeerDescriptor[]): Promise<void>
|
|
6
6
|
hasJoined(): boolean
|
|
7
7
|
getLocalPeerDescriptor(): PeerDescriptor
|
|
8
|
-
getDataFromDht(key:
|
|
9
|
-
storeDataToDht(key:
|
|
10
|
-
deleteDataFromDht(key:
|
|
8
|
+
getDataFromDht(key: DhtAddress): Promise<DataEntry[]>
|
|
9
|
+
storeDataToDht(key: DhtAddress, data: Any): Promise<PeerDescriptor[]>
|
|
10
|
+
deleteDataFromDht(key: DhtAddress, waitForCompletion: boolean): Promise<void>
|
|
11
11
|
waitForNetworkConnectivity(): Promise<void>
|
|
12
12
|
getTransport(): ITransport
|
|
13
13
|
start(): Promise<void>
|
package/src/logic/NodeList.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import { PeerDescriptor } from '@streamr/dht'
|
|
1
|
+
import { DhtAddress, PeerDescriptor, getNodeIdFromPeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { sample } from 'lodash'
|
|
3
3
|
import { DeliveryRpcRemote } from './DeliveryRpcRemote'
|
|
4
4
|
import { EventEmitter } from 'eventemitter3'
|
|
5
|
-
import { getNodeIdFromPeerDescriptor, NodeID } from '../identifiers'
|
|
6
5
|
|
|
7
6
|
export interface Events {
|
|
8
|
-
nodeAdded: (id:
|
|
7
|
+
nodeAdded: (id: DhtAddress, remote: DeliveryRpcRemote) => any
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
const getValuesOfIncludedKeys = (nodes: Map<
|
|
10
|
+
const getValuesOfIncludedKeys = (nodes: Map<DhtAddress, DeliveryRpcRemote>, exclude: DhtAddress[]): DeliveryRpcRemote[] => {
|
|
12
11
|
return Array.from(nodes.entries())
|
|
13
12
|
.filter(([id, _node]) => !exclude.includes(id))
|
|
14
13
|
.map(([_id, node]) => node)
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
export class NodeList extends EventEmitter<Events> {
|
|
18
|
-
private readonly nodes: Map<
|
|
17
|
+
private readonly nodes: Map<DhtAddress, DeliveryRpcRemote>
|
|
19
18
|
private readonly limit: number
|
|
20
|
-
private ownId:
|
|
19
|
+
private ownId: DhtAddress
|
|
21
20
|
|
|
22
|
-
constructor(ownId:
|
|
21
|
+
constructor(ownId: DhtAddress, limit: number) {
|
|
23
22
|
super()
|
|
24
23
|
this.nodes = new Map()
|
|
25
24
|
this.limit = limit
|
|
@@ -42,7 +41,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
42
41
|
this.nodes.delete(getNodeIdFromPeerDescriptor(peerDescriptor))
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
removeById(nodeId:
|
|
44
|
+
removeById(nodeId: DhtAddress): void {
|
|
46
45
|
this.nodes.delete(nodeId)
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -50,7 +49,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
50
49
|
return this.nodes.has(getNodeIdFromPeerDescriptor(peerDescriptor))
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
hasNodeById(nodeId:
|
|
52
|
+
hasNodeById(nodeId: DhtAddress): boolean {
|
|
54
53
|
return this.nodes.has(nodeId)
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -62,28 +61,28 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
62
61
|
})
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
getIds():
|
|
64
|
+
getIds(): DhtAddress[] {
|
|
66
65
|
return Array.from(this.nodes.keys())
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
get(id:
|
|
68
|
+
get(id: DhtAddress): DeliveryRpcRemote | undefined {
|
|
70
69
|
return this.nodes.get(id)
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
size(exclude:
|
|
72
|
+
size(exclude: DhtAddress[] = []): number {
|
|
74
73
|
return Array.from(this.nodes.keys()).filter((node) => !exclude.includes(node)).length
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
getRandom(exclude:
|
|
76
|
+
getRandom(exclude: DhtAddress[]): DeliveryRpcRemote | undefined {
|
|
78
77
|
return sample(getValuesOfIncludedKeys(this.nodes, exclude))
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
getClosest(exclude:
|
|
80
|
+
getClosest(exclude: DhtAddress[]): DeliveryRpcRemote | undefined {
|
|
82
81
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
83
82
|
return included[0]
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
getClosestAndFurthest(exclude:
|
|
85
|
+
getClosestAndFurthest(exclude: DhtAddress[]): DeliveryRpcRemote[] {
|
|
87
86
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
88
87
|
if (included.length === 0) {
|
|
89
88
|
return []
|
|
@@ -91,7 +90,7 @@ export class NodeList extends EventEmitter<Events> {
|
|
|
91
90
|
return included.length > 1 ? [this.getClosest(exclude)!, this.getFurthest(exclude)!] : [this.getClosest(exclude)!]
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
getFurthest(exclude:
|
|
93
|
+
getFurthest(exclude: DhtAddress[]): DeliveryRpcRemote | undefined {
|
|
95
94
|
const included = getValuesOfIncludedKeys(this.nodes, exclude)
|
|
96
95
|
return included[included.length - 1]
|
|
97
96
|
}
|
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
PeerDescriptor,
|
|
4
4
|
ListeningRpcCommunicator,
|
|
5
5
|
ITransport,
|
|
6
|
-
ConnectionLocker
|
|
6
|
+
ConnectionLocker,
|
|
7
|
+
DhtAddress,
|
|
8
|
+
getNodeIdFromPeerDescriptor
|
|
7
9
|
} from '@streamr/dht'
|
|
8
10
|
import {
|
|
9
11
|
StreamMessage,
|
|
@@ -28,14 +30,13 @@ import { ProxyConnectionRpcLocal } from './proxy/ProxyConnectionRpcLocal'
|
|
|
28
30
|
import { Inspector } from './inspect/Inspector'
|
|
29
31
|
import { TemporaryConnectionRpcLocal } from './temporary-connection/TemporaryConnectionRpcLocal'
|
|
30
32
|
import { markAndCheckDuplicate } from './utils'
|
|
31
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
32
33
|
import { Layer1Node } from './Layer1Node'
|
|
33
34
|
import { StreamPartID } from '@streamr/protocol'
|
|
34
35
|
import { uniqBy } from 'lodash'
|
|
35
36
|
|
|
36
37
|
export interface Events {
|
|
37
38
|
message: (message: StreamMessage) => void
|
|
38
|
-
targetNeighborConnected: (nodeId:
|
|
39
|
+
targetNeighborConnected: (nodeId: DhtAddress) => void
|
|
39
40
|
entryPointLeaveDetected: () => void
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -82,8 +83,8 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
82
83
|
streamPartId: this.config.streamPartId,
|
|
83
84
|
rpcCommunicator: this.config.rpcCommunicator,
|
|
84
85
|
markAndCheckDuplicate: (msg: MessageID, prev?: MessageRef) => markAndCheckDuplicate(this.duplicateDetectors, msg, prev),
|
|
85
|
-
broadcast: (message: StreamMessage, previousNode?:
|
|
86
|
-
onLeaveNotice: (sourceId:
|
|
86
|
+
broadcast: (message: StreamMessage, previousNode?: DhtAddress) => this.broadcast(message, previousNode),
|
|
87
|
+
onLeaveNotice: (sourceId: DhtAddress, sourceIsStreamEntryPoint: boolean) => {
|
|
87
88
|
if (this.abortController.signal.aborted) {
|
|
88
89
|
return
|
|
89
90
|
}
|
|
@@ -104,7 +105,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
104
105
|
this.emit('entryPointLeaveDetected')
|
|
105
106
|
}
|
|
106
107
|
},
|
|
107
|
-
markForInspection: (senderId:
|
|
108
|
+
markForInspection: (senderId: DhtAddress, messageId: MessageID) => this.config.inspector.markMessage(senderId, messageId)
|
|
108
109
|
})
|
|
109
110
|
}
|
|
110
111
|
|
|
@@ -154,7 +155,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
154
155
|
addManagedEventListener(
|
|
155
156
|
this.config.proxyConnectionRpcLocal,
|
|
156
157
|
'newConnection',
|
|
157
|
-
(id:
|
|
158
|
+
(id: DhtAddress) => this.config.propagation.onNeighborJoined(id),
|
|
158
159
|
this.abortController.signal
|
|
159
160
|
)
|
|
160
161
|
}
|
|
@@ -199,7 +200,6 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
199
200
|
new DeliveryRpcRemote(
|
|
200
201
|
this.config.localPeerDescriptor,
|
|
201
202
|
descriptor,
|
|
202
|
-
this.config.streamPartId,
|
|
203
203
|
this.config.rpcCommunicator,
|
|
204
204
|
DeliveryRpcClient,
|
|
205
205
|
this.config.rpcRequestTimeout
|
|
@@ -213,7 +213,6 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
213
213
|
new DeliveryRpcRemote(
|
|
214
214
|
this.config.localPeerDescriptor,
|
|
215
215
|
descriptor,
|
|
216
|
-
this.config.streamPartId,
|
|
217
216
|
this.config.rpcCommunicator,
|
|
218
217
|
DeliveryRpcClient,
|
|
219
218
|
this.config.rpcRequestTimeout
|
|
@@ -231,7 +230,6 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
231
230
|
new DeliveryRpcRemote(
|
|
232
231
|
this.config.localPeerDescriptor,
|
|
233
232
|
descriptor,
|
|
234
|
-
this.config.streamPartId,
|
|
235
233
|
this.config.rpcCommunicator,
|
|
236
234
|
DeliveryRpcClient,
|
|
237
235
|
this.config.rpcRequestTimeout
|
|
@@ -251,7 +249,6 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
251
249
|
new DeliveryRpcRemote(
|
|
252
250
|
this.config.localPeerDescriptor,
|
|
253
251
|
descriptor,
|
|
254
|
-
this.config.streamPartId,
|
|
255
252
|
this.config.rpcCommunicator,
|
|
256
253
|
DeliveryRpcClient,
|
|
257
254
|
this.config.rpcRequestTimeout
|
|
@@ -279,7 +276,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
279
276
|
return uniqBy(nodes, (p) => getNodeIdFromPeerDescriptor(p))
|
|
280
277
|
}
|
|
281
278
|
|
|
282
|
-
hasProxyConnection(nodeId:
|
|
279
|
+
hasProxyConnection(nodeId: DhtAddress): boolean {
|
|
283
280
|
if (this.config.proxyConnectionRpcLocal) {
|
|
284
281
|
return this.config.proxyConnectionRpcLocal.hasConnection(nodeId)
|
|
285
282
|
}
|
|
@@ -292,7 +289,9 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
292
289
|
}
|
|
293
290
|
this.abortController.abort()
|
|
294
291
|
this.config.proxyConnectionRpcLocal?.stop()
|
|
295
|
-
this.config.targetNeighbors.getAll().map(
|
|
292
|
+
this.config.targetNeighbors.getAll().map(
|
|
293
|
+
(remote) => remote.leaveStreamPartNotice(this.config.streamPartId, this.config.isLocalNodeEntryPoint())
|
|
294
|
+
)
|
|
296
295
|
this.config.rpcCommunicator.destroy()
|
|
297
296
|
this.removeAllListeners()
|
|
298
297
|
this.config.nearbyNodeView.stop()
|
|
@@ -303,7 +302,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
303
302
|
this.config.inspector.stop()
|
|
304
303
|
}
|
|
305
304
|
|
|
306
|
-
broadcast(msg: StreamMessage, previousNode?:
|
|
305
|
+
broadcast(msg: StreamMessage, previousNode?: DhtAddress): void {
|
|
307
306
|
if (!previousNode) {
|
|
308
307
|
markAndCheckDuplicate(this.duplicateDetectors, msg.messageId!, msg.previousMessageRef)
|
|
309
308
|
}
|
|
@@ -315,7 +314,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
315
314
|
return this.config.inspector.inspect(peerDescriptor)
|
|
316
315
|
}
|
|
317
316
|
|
|
318
|
-
private getPropagationTargets(msg: StreamMessage):
|
|
317
|
+
private getPropagationTargets(msg: StreamMessage): DhtAddress[] {
|
|
319
318
|
let propagationTargets = this.config.targetNeighbors.getIds()
|
|
320
319
|
if (this.config.proxyConnectionRpcLocal) {
|
|
321
320
|
propagationTargets = propagationTargets.concat(this.config.proxyConnectionRpcLocal.getPropagationTargets(msg))
|
|
@@ -325,7 +324,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
325
324
|
return propagationTargets
|
|
326
325
|
}
|
|
327
326
|
|
|
328
|
-
getOwnNodeId():
|
|
327
|
+
getOwnNodeId(): DhtAddress {
|
|
329
328
|
return getNodeIdFromPeerDescriptor(this.config.localPeerDescriptor)
|
|
330
329
|
}
|
|
331
330
|
|
|
@@ -333,7 +332,7 @@ export class RandomGraphNode extends EventEmitter<Events> {
|
|
|
333
332
|
return this.config.handshaker.getOngoingHandshakes().size
|
|
334
333
|
}
|
|
335
334
|
|
|
336
|
-
getTargetNeighborIds():
|
|
335
|
+
getTargetNeighborIds(): DhtAddress[] {
|
|
337
336
|
if (!this.started && this.isStopped()) {
|
|
338
337
|
return []
|
|
339
338
|
}
|
package/src/logic/StreamrNode.ts
CHANGED
|
@@ -3,7 +3,9 @@ import {
|
|
|
3
3
|
DhtNode,
|
|
4
4
|
ITransport,
|
|
5
5
|
PeerDescriptor,
|
|
6
|
-
EXISTING_CONNECTION_TIMEOUT
|
|
6
|
+
EXISTING_CONNECTION_TIMEOUT,
|
|
7
|
+
DhtAddress,
|
|
8
|
+
getNodeIdFromPeerDescriptor
|
|
7
9
|
} from '@streamr/dht'
|
|
8
10
|
import { StreamID, StreamPartID, StreamPartIDUtils, toStreamPartID } from '@streamr/protocol'
|
|
9
11
|
import {
|
|
@@ -16,7 +18,6 @@ import {
|
|
|
16
18
|
} from '@streamr/utils'
|
|
17
19
|
import { EventEmitter } from 'eventemitter3'
|
|
18
20
|
import { sampleSize } from 'lodash'
|
|
19
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
20
21
|
import { ProxyDirection, StreamMessage } from '../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
21
22
|
import { Layer0Node } from './Layer0Node'
|
|
22
23
|
import { Layer1Node } from './Layer1Node'
|
|
@@ -109,6 +110,7 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
109
110
|
|
|
110
111
|
broadcast(msg: StreamMessage): void {
|
|
111
112
|
const streamPartId = toStreamPartID(msg.messageId!.streamId as StreamID, msg.messageId!.streamPartition)
|
|
113
|
+
logger.debug(`Broadcasting to stream part ${streamPartId}`)
|
|
112
114
|
this.joinStreamPart(streamPartId)
|
|
113
115
|
this.streamParts.get(streamPartId)!.broadcast(msg)
|
|
114
116
|
this.metrics.broadcastMessagesPerSecond.record(1)
|
|
@@ -124,11 +126,11 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
joinStreamPart(streamPartId: StreamPartID): void {
|
|
127
|
-
logger.debug(`Join stream part ${streamPartId}`)
|
|
128
129
|
let streamPart = this.streamParts.get(streamPartId)
|
|
129
130
|
if (streamPart !== undefined) {
|
|
130
131
|
return
|
|
131
132
|
}
|
|
133
|
+
logger.debug(`Join stream part ${streamPartId}`)
|
|
132
134
|
const layer1Node = this.createLayer1Node(streamPartId, this.knownStreamPartEntryPoints.get(streamPartId) ?? [])
|
|
133
135
|
const entryPointDiscovery = new EntryPointDiscovery({
|
|
134
136
|
streamPartId,
|
|
@@ -136,7 +138,7 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
136
138
|
layer1Node,
|
|
137
139
|
getEntryPointData: (key) => this.layer0Node!.getDataFromDht(key),
|
|
138
140
|
storeEntryPointData: (key, data) => this.layer0Node!.storeDataToDht(key, data),
|
|
139
|
-
deleteEntryPointData: async (key
|
|
141
|
+
deleteEntryPointData: async (key) => this.layer0Node!.deleteDataFromDht(key, false)
|
|
140
142
|
})
|
|
141
143
|
const node = this.createRandomGraphNode(
|
|
142
144
|
streamPartId,
|
|
@@ -305,11 +307,11 @@ export class StreamrNode extends EventEmitter<Events> {
|
|
|
305
307
|
return this.layer0Node!.getLocalPeerDescriptor()
|
|
306
308
|
}
|
|
307
309
|
|
|
308
|
-
getNodeId():
|
|
310
|
+
getNodeId(): DhtAddress {
|
|
309
311
|
return getNodeIdFromPeerDescriptor(this.layer0Node!.getLocalPeerDescriptor())
|
|
310
312
|
}
|
|
311
313
|
|
|
312
|
-
getNeighbors(streamPartId: StreamPartID):
|
|
314
|
+
getNeighbors(streamPartId: StreamPartID): DhtAddress[] {
|
|
313
315
|
const streamPart = this.streamParts.get(streamPartId)
|
|
314
316
|
return (streamPart !== undefined) && (streamPart.proxied === false)
|
|
315
317
|
? streamPart.node.getTargetNeighborIds()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ListeningRpcCommunicator } from '@streamr/dht'
|
|
1
|
+
import { DhtAddress, ListeningRpcCommunicator, getNodeIdFromPeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { Handshaker } from './neighbor-discovery/Handshaker'
|
|
3
3
|
import { NeighborFinder } from './neighbor-discovery/NeighborFinder'
|
|
4
4
|
import { NeighborUpdateManager } from './neighbor-discovery/NeighborUpdateManager'
|
|
@@ -10,7 +10,6 @@ import { MarkOptional } from 'ts-essentials'
|
|
|
10
10
|
import { ProxyConnectionRpcLocal } from './proxy/ProxyConnectionRpcLocal'
|
|
11
11
|
import { Inspector } from './inspect/Inspector'
|
|
12
12
|
import { TemporaryConnectionRpcLocal } from './temporary-connection/TemporaryConnectionRpcLocal'
|
|
13
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../identifiers'
|
|
14
13
|
import { formStreamPartDeliveryServiceId } from './formStreamPartDeliveryServiceId'
|
|
15
14
|
|
|
16
15
|
type RandomGraphNodeConfig = MarkOptional<StrictRandomGraphNodeConfig,
|
|
@@ -40,7 +39,6 @@ const createConfigWithDefaults = (config: RandomGraphNodeConfig): StrictRandomGr
|
|
|
40
39
|
const targetNeighbors = config.targetNeighbors ?? new NodeList(ownNodeId, maxNumberOfContacts)
|
|
41
40
|
|
|
42
41
|
const temporaryConnectionRpcLocal = new TemporaryConnectionRpcLocal({
|
|
43
|
-
streamPartId: config.streamPartId,
|
|
44
42
|
rpcCommunicator,
|
|
45
43
|
localPeerDescriptor: config.localPeerDescriptor
|
|
46
44
|
})
|
|
@@ -51,7 +49,7 @@ const createConfigWithDefaults = (config: RandomGraphNodeConfig): StrictRandomGr
|
|
|
51
49
|
}) : undefined
|
|
52
50
|
const propagation = config.propagation ?? new Propagation({
|
|
53
51
|
minPropagationTargets,
|
|
54
|
-
sendToNeighbor: async (neighborId:
|
|
52
|
+
sendToNeighbor: async (neighborId: DhtAddress, msg: StreamMessage): Promise<void> => {
|
|
55
53
|
const remote = targetNeighbors.get(neighborId) ?? temporaryConnectionRpcLocal.getNodes().get(neighborId)
|
|
56
54
|
const proxyConnection = proxyConnectionRpcLocal?.getConnection(neighborId)
|
|
57
55
|
if (remote) {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { EventEmitter } from 'eventemitter3'
|
|
2
|
-
import { NodeID } from '../../identifiers'
|
|
3
2
|
import { MessageID } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
4
3
|
import { binaryToHex } from '@streamr/utils'
|
|
4
|
+
import { DhtAddress } from '@streamr/dht'
|
|
5
5
|
|
|
6
6
|
export interface Events {
|
|
7
7
|
done: () => void
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
interface InspectSessionConfig {
|
|
11
|
-
inspectedNode:
|
|
11
|
+
inspectedNode: DhtAddress
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const createMessageKey = (messageId: MessageID): string => {
|
|
@@ -18,14 +18,14 @@ export class InspectSession extends EventEmitter<Events> {
|
|
|
18
18
|
|
|
19
19
|
// Boolean indicates if the message has been received by the inspected node
|
|
20
20
|
private readonly inspectionMessages: Map<string, boolean> = new Map()
|
|
21
|
-
private readonly inspectedNode:
|
|
21
|
+
private readonly inspectedNode: DhtAddress
|
|
22
22
|
|
|
23
23
|
constructor(config: InspectSessionConfig) {
|
|
24
24
|
super()
|
|
25
25
|
this.inspectedNode = config.inspectedNode
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
markMessage(senderId:
|
|
28
|
+
markMessage(senderId: DhtAddress, messageId: MessageID): void {
|
|
29
29
|
const messageKey = createMessageKey(messageId)
|
|
30
30
|
if (!this.inspectionMessages.has(messageKey)) {
|
|
31
31
|
this.inspectionMessages.set(messageKey, senderId === this.inspectedNode)
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { PeerDescriptor, ConnectionLocker, LockID } from '@streamr/dht'
|
|
1
|
+
import { PeerDescriptor, ConnectionLocker, LockID, DhtAddress, getNodeIdFromPeerDescriptor } from '@streamr/dht'
|
|
2
2
|
import { MessageID } from '../../proto/packages/trackerless-network/protos/NetworkRpc'
|
|
3
3
|
import { InspectSession, Events as InspectSessionEvents } from './InspectSession'
|
|
4
4
|
import { TemporaryConnectionRpcClient } from '../../proto/packages/trackerless-network/protos/NetworkRpc.client'
|
|
5
5
|
import { RpcCommunicator } from '@streamr/proto-rpc'
|
|
6
6
|
import { Logger, waitForEvent3 } from '@streamr/utils'
|
|
7
7
|
import { TemporaryConnectionRpcRemote } from '../temporary-connection/TemporaryConnectionRpcRemote'
|
|
8
|
-
import { NodeID, getNodeIdFromPeerDescriptor } from '../../identifiers'
|
|
9
8
|
import { StreamPartID } from '@streamr/protocol'
|
|
10
9
|
|
|
11
10
|
interface InspectorConfig {
|
|
@@ -22,7 +21,7 @@ const DEFAULT_TIMEOUT = 60 * 1000
|
|
|
22
21
|
|
|
23
22
|
export class Inspector {
|
|
24
23
|
|
|
25
|
-
private readonly sessions: Map<
|
|
24
|
+
private readonly sessions: Map<DhtAddress, InspectSession> = new Map()
|
|
26
25
|
private readonly streamPartId: StreamPartID
|
|
27
26
|
private readonly localPeerDescriptor: PeerDescriptor
|
|
28
27
|
private readonly rpcCommunicator: RpcCommunicator
|
|
@@ -43,7 +42,6 @@ export class Inspector {
|
|
|
43
42
|
const rpcRemote = new TemporaryConnectionRpcRemote(
|
|
44
43
|
this.localPeerDescriptor,
|
|
45
44
|
peerDescriptor,
|
|
46
|
-
this.streamPartId,
|
|
47
45
|
this.rpcCommunicator,
|
|
48
46
|
TemporaryConnectionRpcClient
|
|
49
47
|
)
|
|
@@ -72,11 +70,11 @@ export class Inspector {
|
|
|
72
70
|
return success || session.getInspectedMessageCount() < 1
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
markMessage(sender:
|
|
73
|
+
markMessage(sender: DhtAddress, messageId: MessageID): void {
|
|
76
74
|
this.sessions.forEach((session) => session.markMessage(sender, messageId))
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
isInspected(nodeId:
|
|
77
|
+
isInspected(nodeId: DhtAddress): boolean {
|
|
80
78
|
return this.sessions.has(nodeId)
|
|
81
79
|
}
|
|
82
80
|
|