Skip to content

Commit

Permalink
GOLD-290 : Blacklisting of IPs in the Json RPC server which have caus…
Browse files Browse the repository at this point in the history
…ed timeouts (#71)

* feat(handlers): added a wrapped handler which logs and returns 500s in case of errors

feat(middleware): removed unused test middleware

feat(axios): added configurable axios timeout set to 5s

feat(config): updated axios timeout to 3s

docs(todo): added todo to add a blacklist

feat: Add TTLMap for blacklisted IP entries

The code changes introduce a new `TTLMap` class in the `utils` directory. This class is used to store blacklisted IP entries with a time-to-live (TTL) value. The TTLMap allows for the expiration of blacklisted IP entries after a certain period of time.

This change is necessary to implement the feature of blacklisting IP addresses that cause timeouts. The `TTLMap` is used to store the blacklisted IP entries along with their expiration time. When a timeout occurs, the IP address is added to the `TTLMap` with a TTL value calculated based on the axios timeout configuration.

This commit message follows the established convention of starting with a verb in the imperative form ("Add") and providing a concise and clear description of the changes made.

feat: Update blacklisted IP entry expiration time and added logs.

The code changes modify the `retainTimedOutEntriesForMillis` constant in the `api.ts` file. The value is updated to `config.axiosTimeoutInMs * 10` to increase the expiration time of blacklisted IP entries from 3 times the axios timeout to 10 times the axios timeout.

This change is necessary to ensure that blacklisted IP entries remain in the `TTLMap` for a longer period of time before being expired. This allows for better handling of IP addresses that cause timeouts.

This commit message follows the established convention of starting with a verb in the imperative form ("Update") and providing a concise and clear description of the changes made.

feat: Increase expiration time of blacklisted IP entries

The code changes update the `retainTimedOutEntriesForMillis` constant in the `api.ts` file. The value is changed to `config.axiosTimeoutInMs * 100` to increase the expiration time of blacklisted IP entries from 5 minutes to 50 minutes.

This change is necessary to ensure that blacklisted IP entries remain in the `TTLMap` for a longer period of time before being expired. This allows for better handling of IP addresses that cause timeouts.

* feat : add counters for blacklistedIp
  • Loading branch information
abdulazeem-tk4vr authored Oct 18, 2024
1 parent f8b7769 commit f3415d6
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
31 changes: 29 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { bytesToHex, toBytes } from '@ethereumjs/util'
import { RLP } from '@ethereumjs/rlp'
import { nestedCountersInstance } from './utils/nestedCounters'
import { trySpendServicePoints } from './utils/servicePoints'
import { TTLMap } from './utils/TTLMap'

export const verbose = config.verbose
export const firstLineLogs = config.firstLineLogs
Expand Down Expand Up @@ -113,6 +114,13 @@ export type DetailedTxStatus = {

type JsonValue = string | number | boolean | null | undefined | JsonValue[] | { [key: string]: JsonValue }

const retainTimedOutEntriesForMillis = config.axiosTimeoutInMs * 100 // 300 s = 5 minutes
// added for expiration of blacklisted IP entries
const blacklistedIPMapping = new TTLMap<{
baseUrl: string
blackListedAt: number
}>()

// [] ask about this with Thant
type TransactionData = {
raw?: string
Expand Down Expand Up @@ -573,7 +581,21 @@ async function injectAndRecordTx(
status: number
}> {
const { raw } = tx
const { baseUrl } = getBaseUrl()
let nodeIpPort: string, baseUrl: string

// Initialize them with some values({ nodeIpPort, baseUrl } = getBaseUrl())

if (config.enableBlacklistingIP) {
// Continuously reassign nodeIpPort and baseUrl until a non-blacklisted one is found
let entry

do {
;({ nodeIpPort, baseUrl } = getBaseUrl())
entry = blacklistedIPMapping.get(nodeIpPort)
if (entry !== undefined) console.log('The ip address blacklisted is', nodeIpPort)
} while (entry !== undefined)
}

totalResult += 1
const startTime = Date.now()

Expand Down Expand Up @@ -712,8 +734,13 @@ async function injectAndRecordTx(
})
.catch((e: Error) => {
if (e.message.includes('timeout')) {
// TODO: add to blacklist with a TTL and use this blacklist to avoid bad node selction for inject.
blacklistedIPMapping.set(
nodeIpPort,
{ baseUrl: baseUrl, blackListedAt: Date.now() },
retainTimedOutEntriesForMillis
)
console.log(`injectAndRecordTx: transaction timed out ip: ${baseUrl}, e: ${e.message}`)
nestedCountersInstance.countEvent('validatorBlacklist', nodeIpPort)
}
if (config.verbose) console.log('injectAndRecordTx: Caught Exception: ' + e.message)
countInjectTxRejections('Caught Exception: ' + trimInjectRejection(e.message))
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ type Config = {
limit: number // max requests per IP within time window
}
axiosTimeoutInMs: number
enableBlacklistingIP: boolean
}

export type ServicePointTypes = 'aalg-warmup'
Expand Down Expand Up @@ -247,4 +248,5 @@ export const CONFIG: Config = {
limit: 100, // 100 requests per IP
},
axiosTimeoutInMs: 3000,
enableBlacklistingIP: false,
}
39 changes: 39 additions & 0 deletions src/utils/TTLMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export interface TTLMapValue<T> {
value: T
expiry: number
timeoutId?: NodeJS.Timeout
}

export type OnExpiryCallback<T> = (key: string, value: T) => void

export class TTLMap<T> {
private readonly map: { [key: string]: TTLMapValue<T> } = {}

public set(key: string, value: T, ttl: number, onExpiry?: OnExpiryCallback<T>): void {
const expiry = Date.now() + ttl
const timeoutId = setTimeout(() => {
if (onExpiry) {
onExpiry(key, value)
}
delete this.map[key]

Check warning on line 18 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Generic Object Injection Sink
}, ttl)
this.map[key] = { value, expiry, timeoutId }

Check warning on line 20 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Generic Object Injection Sink
}

public get(key: string): T | undefined {
const value = this.map[key]

Check warning on line 24 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Variable Assigned to Object Injection Sink
if (value && value.expiry > Date.now()) {
return value.value
}
delete this.map[key]

Check warning on line 28 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Generic Object Injection Sink
return undefined
}

public delete(key: string): void {
const entry = this.map[key]

Check warning on line 33 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Variable Assigned to Object Injection Sink
if (entry && entry.timeoutId) {
clearTimeout(entry.timeoutId)
}
delete this.map[key]

Check warning on line 37 in src/utils/TTLMap.ts

View workflow job for this annotation

GitHub Actions / ci / QA merge checks

Generic Object Injection Sink
}
}
2 changes: 1 addition & 1 deletion src/websocket/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import WebSocket from 'ws'
import EventEmitter from 'events'
import { wrappedMethods } from '../api'
import { methods, wrappedMethods } from '../api'
import { logSubscriptionList } from './clients'
import * as crypto from 'crypto'
import { CONFIG } from '../config'
Expand Down

0 comments on commit f3415d6

Please sign in to comment.