diff --git a/packages/client/src/rpc/modules/admin.ts b/packages/client/src/rpc/modules/admin.ts index 49a5c31234..5b94d3b658 100644 --- a/packages/client/src/rpc/modules/admin.ts +++ b/packages/client/src/rpc/modules/admin.ts @@ -6,6 +6,7 @@ import { middleware } from '../validation.js' import type { Chain } from '../../blockchain/index.js' import type { EthereumClient } from '../../client.js' +import type { RlpxPeer } from '../../net/peer/rlpxpeer.js' import type { Service } from '../../service/index.js' /** @@ -28,14 +29,14 @@ export class Admin { this._rpcDebug = rpcDebug this.nodeInfo = middleware(callWithStackTrace(this.nodeInfo.bind(this), this._rpcDebug), 0, []) + this.peers = middleware(callWithStackTrace(this.peers.bind(this), this._rpcDebug), 0, []) } /** * Returns information about the currently running node. - * see for reference: https://geth.ethereum.org/docs/rpc/ns-admin#admin_nodeinfo - * @param params An empty array + * see for reference: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin#admin_peers */ - async nodeInfo(_params: []) { + async nodeInfo() { const rlpxInfo = this._client.config.server!.getRlpxInfo() const { enode, id, ip, listenAddr, ports } = rlpxInfo const { discovery, listener } = ports @@ -68,4 +69,34 @@ export class Admin { } return nodeInfo } + + /** + * Returns information about currently connected peers + * @returns an array of objects containing information about peers (including id, eth protocol versions supported, client name, etc.) + */ + async peers() { + const peers = this._client.services.filter((serv) => serv.name === 'eth')[0]?.pool + .peers as RlpxPeer[] + + return peers?.map((peer) => { + return { + id: peer.id, + // Typescript complains about the typing of `_hello` if we make rlpxPeer possibly null + name: (peer.rlpxPeer as any)['_hello'].clientId ?? null, + protocols: { + eth: { + head: peer.eth?.updatedBestHeader + ? bytesToHex(peer.eth.updatedBestHeader?.hash()) + : bytesToHex(peer.eth?.status.bestHash), + difficulty: peer.eth?.status.td.toString(10), + version: peer.eth?.['versions'].slice(-1)[0] ?? null, + }, + }, + caps: peer.eth?.['versions'].map((ver) => 'eth/' + ver), + network: { + remoteAddress: peer.address, + }, + } + }) + } } diff --git a/packages/client/test/rpc/admin/peers.spec.ts b/packages/client/test/rpc/admin/peers.spec.ts new file mode 100644 index 0000000000..c7bf6f40f8 --- /dev/null +++ b/packages/client/test/rpc/admin/peers.spec.ts @@ -0,0 +1,38 @@ +import { randomBytes } from 'crypto' +import { assert, describe, it } from 'vitest' + +import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' + +const method = 'admin_peers' + +describe(method, () => { + it('works', async () => { + const manager = createManager(await createClient({ opened: true, noPeers: true })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + console.log(manager['_client'].services[0].pool) + //@ts-ignore + manager['_client'].services[0].pool.peers = [ + { + id: 'abcd', + eth: { + versions: ['68'], + status: { + td: 1n, + bestHash: randomBytes(32), + }, + }, + rlpxPeer: { + _hello: { + clientId: 'fakeClient', + }, + }, + address: '127.0.0.1:8545', + }, + ] + const res = await rpc.request(method, []) + const { result } = res + console.log(res) + assert.notEqual(result, undefined, 'admin_peers returns a value') + }) +})