Skip to content

Commit 3393430

Browse files
committed
Merge branch 'main' into dependabot/npm_and_yarn/dashboard/secp256k1-5.0.1
2 parents 46fddbe + c94b0dd commit 3393430

File tree

17 files changed

+416
-141
lines changed

17 files changed

+416
-141
lines changed

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export INDEXER_INTERVAL=
2727
export ALLOWED_ADMINS=
2828
export DASHBOARD=true
2929
export RATE_DENY_LIST=
30-
export MAX_REQ_PER_SECOND=
30+
export MAX_REQ_PER_MINUTE=
3131
export MAX_CHECKSUM_LENGTH=
3232
export LOG_LEVEL=
3333
export HTTP_API_PORT=

.github/workflows/ci.yml

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,6 @@ jobs:
132132
- run: docker image ls
133133
- name: Delete default runner images
134134
run: |
135-
docker image rm node:20
136-
docker image rm node:20-alpine
137-
docker image rm node:18
138-
docker image rm node:18-alpine
139-
docker image rm debian:10
140-
docker image rm debian:11
141-
docker image rm ubuntu:22.04
142-
docker image rm ubuntu:20.04
143-
docker image rm moby/buildkit:latest
144135
rm -rf /usr/share/swift/
145136
- name: Wait for contracts deployment and C2D cluster to be ready
146137
working-directory: ${{ github.workspace }}/barge
@@ -226,15 +217,6 @@ jobs:
226217
- run: docker image ls
227218
- name: Delete default runner images
228219
run: |
229-
docker image rm node:20
230-
docker image rm node:20-alpine
231-
docker image rm node:18
232-
docker image rm node:18-alpine
233-
docker image rm debian:10
234-
docker image rm debian:11
235-
docker image rm ubuntu:22.04
236-
docker image rm ubuntu:20.04
237-
docker image rm moby/buildkit:latest
238220
rm -rf /usr/share/swift/
239221
240222
- name: Wait for contracts deployment and C2D cluster to be ready
@@ -278,6 +260,8 @@ jobs:
278260
P2P_ENABLE_AUTONAT: 'false'
279261
ALLOWED_ADMINS: '["0xe2DD09d719Da89e5a3D0F2549c7E24566e947260"]'
280262
DB_TYPE: 'elasticsearch'
263+
MAX_REQ_PER_MINUTE: 320
264+
MAX_CONNECTIONS_PER_MINUTE: 320
281265
- name: Check Ocean Node is running
282266
run: |
283267
for i in $(seq 1 90); do

docs/API.md

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,38 @@ returns P2P peer
449449

450450
---
451451

452+
## find peer multiaddress
453+
454+
### `HTTP` GET /findPeer/?
455+
456+
#### Description
457+
458+
returns P2P peer multiaddresses if found in DHT
459+
460+
#### Query Parameters
461+
462+
| name | type | required | description |
463+
| ------- | ------ | -------- | ----------- |
464+
| peerId | string | v | peer id |
465+
| timeout | int | optional | timeout |
466+
467+
#### Response
468+
469+
```
470+
{
471+
"id": "16Uiu2HAmLhRDqfufZiQnxvQs2XHhd6hwkLSPfjAQg1gH8wgRixiP",
472+
"multiaddrs": [
473+
"/ip4/127.0.0.1/tcp/9000",
474+
"/ip4/127.0.0.1/tcp/9001/ws",
475+
"/ip4/172.18.0.2/tcp/9000",
476+
"/ip4/172.18.0.2/tcp/9001/ws",
477+
"/ip6/::1/tcp/9002"
478+
]
479+
}
480+
```
481+
482+
---
483+
452484
## Get P2P Peers
453485

454486
### `HTTP` GET /getP2PPeers
@@ -501,13 +533,14 @@ returns an empty object if it is valid otherwise an array with error
501533

502534
#### Parameters
503535

504-
| name | type | required | description |
505-
| ---------- | ------ | -------- | ------------------------------------------------- |
506-
| command | string | v | command name |
507-
| node | string | | if not present it means current node |
508-
| id | string | v | document id or did |
509-
| chainId | number | v | chain id of network on which document is provided |
510-
| nftAddress | string | v | address of nft token |
536+
| name | type | required | description |
537+
| ---------- | -------- | -------- | ------------------------------------------------- |
538+
| command | string | v | command name |
539+
| node | string | | if not present it means current node |
540+
| multiAddrs | string[] | | if passed, use this instead of peerStore & DHT |
541+
| id | string | v | document id or did |
542+
| chainId | number | v | chain id of network on which document is provided |
543+
| nftAddress | string | v | address of nft token |
511544

512545
#### Request
513546

docs/dockerDeployment.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ services:
9999
# INDEXER_INTERVAL: ''
100100
DASHBOARD: 'true'
101101
# RATE_DENY_LIST: ''
102-
# MAX_REQ_PER_SECOND: ''
102+
# MAX_REQ_PER_MINUTE: ''
103103
# MAX_CHECKSUM_LENGTH: ''
104104
# LOG_LEVEL: ''
105105
HTTP_API_PORT: '8000'

docs/env.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Environmental variables are also tracked in `ENVIRONMENT_VARIABLES` within `src/
2323
- `ALLOWED_ADMINS`: Sets the public address of accounts which have access to admin endpoints e.g. shutting down the node. Example: `"[\"0x967da4048cD07aB37855c090aAF366e4ce1b9F48\",\"0x388C818CA8B9251b393131C08a736A67ccB19297\"]"`
2424
- `DASHBOARD`: If `false` the dashboard will not run. If not set or `true` the dashboard will start with the node. Example: `false`
2525
- `RATE_DENY_LIST`: Blocked list of IPs and peer IDs. Example: `"{ \"peers\": [\"16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo\"], \"ips\": [\"127.0.0.1\"] }"`
26-
- `MAX_REQ_PER_SECOND`: Number of requests per second allowed by the same client. Example: `3`
26+
- `MAX_REQ_PER_MINUTE`: Number of requests per minute allowed by the same client (IP or Peer id). Example: `30`
27+
- `MAX_CONNECTIONS_PER_MINUTE`: Max number of requests allowed per minute (all clients). Example: `120`
2728
- `MAX_CHECKSUM_LENGTH`: Define the maximum length for a file if checksum is required (Mb). Example: `10`
2829
- `IS_BOOTSTRAP`: Is this node to be used as bootstrap node or not. Default is `false`.
2930

@@ -54,7 +55,7 @@ Environmental variables are also tracked in `ENVIRONMENT_VARIABLES` within `src/
5455
- `P2P_pubsubPeerDiscoveryInterval`: Interval (in ms) for discovery using pubsub. Defaults to `10000` (three seconds). Example: `10000`
5556
- `P2P_dhtMaxInboundStreams`: Maximum number of DHT inbound streams. Defaults to `500`. Example: `500`
5657
- `P2P_dhtMaxOutboundStreams`: Maximum number of DHT outbound streams. Defaults to `500`. Example: `500`
57-
- `P2P_ENABLE_DHT_SERVER`: Enable DHT server mode. This should be enabled for bootstrapers & well established nodes. Default: `false`
58+
- `P2P_DHT_FILTER`: Filter address in DHT. 0 = (Default) No filter 1. Filter private ddresses. 2. Filter public addresses
5859
- `P2P_mDNSInterval`: Interval (in ms) for discovery using mDNS. Defaults to `20000` (20 seconds). Example: `20000`
5960
- `P2P_connectionsMaxParallelDials`: Maximum number of parallel dials. Defaults to `150`. Example: `150`
6061
- `P2P_connectionsDialTimeout`: Timeout for dial commands. Defaults to `10000` (10 seconds). Example: `10000`

scripts/ocean-node-quickstart.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ services:
155155
# INDEXER_INTERVAL: ''
156156
DASHBOARD: 'true'
157157
# RATE_DENY_LIST: ''
158-
# MAX_REQ_PER_SECOND: ''
158+
# MAX_REQ_PER_MINUTE: ''
159+
# MAX_CONNECTIONS_PER_MINUTE: ''
159160
# MAX_CHECKSUM_LENGTH: ''
160161
# LOG_LEVEL: ''
161162
HTTP_API_PORT: '$HTTP_API_PORT'

src/@types/OceanNode.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ export interface OceanNodeKeys {
2323
privateKey: any
2424
ethAddress: string
2525
}
26-
26+
/* eslint-disable no-unused-vars */
27+
export enum dhtFilterMethod {
28+
filterPrivate = 'filterPrivate', // default, remove all private addresses from DHT
29+
filterPublic = 'filterPublic', // remove all public addresses from DHT
30+
filterNone = 'filterNone' // do not remove all any addresses from DHT
31+
}
2732
export interface OceanNodeP2PConfig {
2833
bootstrapNodes: string[]
2934
bootstrapTimeout: number
@@ -41,7 +46,7 @@ export interface OceanNodeP2PConfig {
4146
pubsubPeerDiscoveryInterval: number
4247
dhtMaxInboundStreams: number
4348
dhtMaxOutboundStreams: number
44-
enableDHTServer: boolean
49+
dhtFilter: dhtFilterMethod
4550
mDNSInterval: number
4651
connectionsMaxParallelDials: number
4752
connectionsDialTimeout: number
@@ -91,7 +96,8 @@ export interface OceanNodeConfig {
9196
assetPurgatoryUrl: string
9297
allowedAdmins?: string[]
9398
codeHash?: string
94-
rateLimit?: number
99+
rateLimit?: number // per request ip or peer
100+
maxConnections?: number // global, regardless of client address(es)
95101
denyList?: DenyList
96102
unsafeURLs?: string[]
97103
isBootstrap?: boolean

src/OceanNode.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ import { pipe } from 'it-pipe'
1111
import { GENERIC_EMOJIS, LOG_LEVELS_STR } from './utils/logging/Logger.js'
1212
import { Handler } from './components/core/handler/handler.js'
1313
import { C2DEngines } from './components/c2d/compute_engines.js'
14+
15+
export interface RequestLimiter {
16+
requester: string | string[] // IP address or peer ID
17+
lastRequestTime: number // time of the last request done (in miliseconds)
18+
numRequests: number // number of requests done in the specific time period
19+
}
20+
21+
export interface RequestDataCheck {
22+
valid: boolean
23+
updatedRequestData: RequestLimiter
24+
}
1425
export class OceanNode {
1526
// eslint-disable-next-line no-use-before-define
1627
private static instance: OceanNode
@@ -20,6 +31,7 @@ export class OceanNode {
2031
private c2dEngines: C2DEngines
2132
// requester
2233
private remoteCaller: string | string[]
34+
private requestMap: Map<string, RequestLimiter>
2335
// eslint-disable-next-line no-useless-constructor
2436
private constructor(
2537
private db?: Database,
@@ -28,6 +40,7 @@ export class OceanNode {
2840
private indexer?: OceanIndexer
2941
) {
3042
this.coreHandlers = CoreHandlersRegistry.getInstance(this)
43+
this.requestMap = new Map<string, RequestLimiter>()
3144
if (node) {
3245
node.setCoreHandlers(this.coreHandlers)
3346
}
@@ -95,6 +108,14 @@ export class OceanNode {
95108
return this.remoteCaller
96109
}
97110

111+
public getRequestMapSize(): number {
112+
return this.requestMap.size
113+
}
114+
115+
public getRequestMap(): Map<string, RequestLimiter> {
116+
return this.requestMap
117+
}
118+
98119
/**
99120
* Use this method to direct calls to the node as node cannot dial into itself
100121
* @param message command message

src/components/P2P/handleProtocolCommands.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ import { GENERIC_EMOJIS, LOG_LEVELS_STR } from '../../utils/logging/Logger.js'
99
import StreamConcat from 'stream-concat'
1010
import { Handler } from '../core/handler/handler.js'
1111
import { getConfiguration } from '../../utils/index.js'
12+
import { checkConnectionsRateLimit } from '../httpRoutes/requestValidator.js'
13+
import { CONNECTIONS_RATE_INTERVAL } from '../../utils/constants.js'
14+
import { RequestLimiter } from '../../OceanNode.js'
15+
16+
// hold data about last request made
17+
const connectionsData: RequestLimiter = {
18+
lastRequestTime: Date.now(),
19+
requester: '',
20+
numRequests: 0
21+
}
1222

1323
export class ReadableString extends Readable {
1424
private sent = false
@@ -60,10 +70,14 @@ export async function handleProtocolCommands(otherPeerConnection: any) {
6070
return status
6171
}
6272

63-
const denyList = await (await getConfiguration()).denyList
73+
const configuration = await getConfiguration()
74+
// check deny list configs
75+
const { denyList } = configuration
6476
if (denyList.peers.length > 0) {
6577
if (denyList.peers.includes(remotePeer.toString())) {
66-
P2P_LOGGER.error(`Incoming request denied to peer: ${remotePeer}`)
78+
P2P_LOGGER.warn(
79+
`Incoming request denied to peer: ${remotePeer} (peer its on deny list)`
80+
)
6781

6882
if (connectionStatus === 'open') {
6983
statusStream = new ReadableString(
@@ -79,6 +93,37 @@ export async function handleProtocolCommands(otherPeerConnection: any) {
7993
return
8094
}
8195
}
96+
// check connections rate limit
97+
const requestTime = Date.now()
98+
if (requestTime - connectionsData.lastRequestTime > CONNECTIONS_RATE_INTERVAL) {
99+
// last one was more than 1 minute ago? reset counter
100+
connectionsData.numRequests = 0
101+
}
102+
// always increment counter
103+
connectionsData.numRequests += 1
104+
// update time and requester information
105+
connectionsData.lastRequestTime = requestTime
106+
connectionsData.requester = remoteAddr
107+
108+
// check global rate limits (not ip related)
109+
const requestRateValidation = checkConnectionsRateLimit(configuration, connectionsData)
110+
if (!requestRateValidation.valid) {
111+
P2P_LOGGER.warn(
112+
`Incoming request denied to peer: ${remotePeer} (rate limit exceeded)`
113+
)
114+
if (connectionStatus === 'open') {
115+
statusStream = new ReadableString(
116+
JSON.stringify(buildWrongCommandStatus(403, 'Rate limit exceeded'))
117+
)
118+
try {
119+
await pipe(statusStream, otherPeerConnection.stream.sink)
120+
} catch (e) {
121+
P2P_LOGGER.error(e)
122+
}
123+
}
124+
await closeStreamConnection(otherPeerConnection.connection, remotePeer)
125+
return
126+
}
82127

83128
try {
84129
// eslint-disable-next-line no-unreachable-loop

0 commit comments

Comments
 (0)