Skip to content

Commit 45a4c8c

Browse files
authored
Support for test chains (#785)
* network constructors * cast types * network: networkId by chain object * client: add ChainId as client attribute * client: determine set of Subnetwork classes with ChainId * client: use NetworkIdByChain * CLI: add chainId to config and args * RPC: use chain specific subnetworks * fix
1 parent b8535f6 commit 45a4c8c

15 files changed

Lines changed: 241 additions & 146 deletions

File tree

packages/cli/src/cliArgs.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export const args: ClientOpts = yargs(hideBin(process.argv))
6161
string: true,
6262
optional: true,
6363
})
64+
.option('chainId', {
65+
describe: 'string representation of the chain id (defaults to mainnet)',
66+
choices: ['mainnet', 'sepolia', 'angelfood'],
67+
string: true,
68+
default: 'mainnet',
69+
})
6470
.option('networks', {
6571
describe: 'subnetworks to enable',
6672
string: true,

packages/cli/src/rpc/modules/beacon.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ContentLookup,
88
LightClientUpdatesByRangeKey,
99
NetworkId,
10+
NetworkIdByChain,
1011
type PortalNetwork,
1112
computeLightClientKeyFromPeriod,
1213
getBeaconContentKey,
@@ -31,7 +32,7 @@ export class beacon {
3132
* @param rpcManager RPC client to which the module binds
3233
*/
3334
constructor(client: PortalNetwork, logger: Debugger) {
34-
this._beacon = client.networks.get(NetworkId.BeaconChainNetwork) as BeaconNetwork
35+
this._beacon = client.networks.get(NetworkIdByChain[client.chainId].BeaconChainNetwork) as BeaconNetwork
3536
this.logger = logger.extend('beacon')
3637

3738
this.methods = middleware(this.methods.bind(this), 0, [])

packages/cli/src/rpc/modules/eth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PrefixedHexString } from '@ethereumjs/util'
22
import { bigIntToHex, hexToBytes, intToHex, toBytes } from '@ethereumjs/util'
3-
import { GET_LOGS_BLOCK_RANGE_LIMIT, NetworkId, getLogs } from 'portalnetwork'
3+
import { GET_LOGS_BLOCK_RANGE_LIMIT, NetworkId, NetworkIdByChain, getLogs } from 'portalnetwork'
44

55
import { INTERNAL_ERROR, INVALID_PARAMS } from '../error-code.js'
66
import { jsonRpcLog } from '../types.js'
@@ -26,7 +26,7 @@ export class eth {
2626
*/
2727
constructor(client: PortalNetwork, logger: Debugger) {
2828
this._client = client
29-
this._history = client.networks.get(NetworkId.HistoryNetwork) as HistoryNetwork
29+
this._history = client.networks.get(NetworkIdByChain[client.chainId].HistoryNetwork) as HistoryNetwork
3030
this.logger = logger.extend('eth')
3131

3232
this.getBlockByNumber = middleware(

packages/cli/src/rpc/modules/portal.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
ContentLookup,
1212
FoundContent,
1313
NetworkId,
14+
NetworkIdByChain,
1415
NodeLookup,
1516
decodeExtensionPayloadToJson,
1617
encodeExtensionPayloadFromJson,
@@ -98,9 +99,9 @@ export class portal {
9899

99100
constructor(client: PortalNetwork, logger: Debugger) {
100101
this._client = client
101-
this._history = this._client.networks.get(NetworkId.HistoryNetwork) as HistoryNetwork
102-
this._beacon = this._client.networks.get(NetworkId.BeaconChainNetwork) as BeaconNetwork
103-
this._state = this._client.networks.get(NetworkId.StateNetwork) as StateNetwork
102+
this._history = this._client.networks.get(NetworkIdByChain[client.chainId].HistoryNetwork) as HistoryNetwork
103+
this._beacon = this._client.networks.get(NetworkIdByChain[client.chainId].BeaconChainNetwork) as BeaconNetwork
104+
this._state = this._client.networks.get(NetworkIdByChain[client.chainId].StateNetwork) as StateNetwork
104105
this.logger = logger
105106
this.methods = middleware(this.methods.bind(this), 0, [])
106107

packages/cli/src/rpc/modules/ultralight.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type PrefixedHexString, bytesToHex, hexToBytes } from '@ethereumjs/util'
2-
import { HistoryNetworkContentType, NetworkId } from 'portalnetwork'
2+
import { HistoryNetworkContentType, type NetworkId, NetworkIdByChain } from 'portalnetwork'
33

44
import { INTERNAL_ERROR } from '../error-code.js'
55
import { middleware, validators } from '../validators.js'
@@ -29,9 +29,9 @@ export class ultralight {
2929

3030
constructor(client: PortalNetwork, logger: Debugger) {
3131
this._client = client
32-
this._history = this._client.network()[NetworkId.HistoryNetwork]
33-
this._state = this._client.network()[NetworkId.StateNetwork]
34-
this._beacon = this._client.network()[NetworkId.BeaconChainNetwork]
32+
this._history = this._client.network()[NetworkIdByChain[client.chainId].HistoryNetwork] as HistoryNetwork | undefined
33+
this._state = this._client.network()[NetworkIdByChain[client.chainId].StateNetwork] as StateNetwork | undefined
34+
this._beacon = this._client.network()[NetworkIdByChain[client.chainId].BeaconChainNetwork] as BeaconNetwork | undefined
3535
this.logger = logger
3636
this.methods = middleware(this.methods.bind(this), 0, [])
3737
this.addContentToDB = middleware(this.addContentToDB.bind(this), 2, [
@@ -106,15 +106,15 @@ export class ultralight {
106106
const [networkId, radius] = params
107107
try {
108108
switch (networkId) {
109-
case NetworkId.HistoryNetwork: {
109+
case NetworkIdByChain[this._client.chainId].HistoryNetwork: {
110110
await this._history!.setRadius(2n ** BigInt(Number.parseInt(radius)) - 1n)
111111
return '0x' + this._history!.nodeRadius.toString(16)
112112
}
113-
case NetworkId.StateNetwork: {
113+
case NetworkIdByChain[this._client.chainId].StateNetwork: {
114114
await this._state!.setRadius(2n ** BigInt(Number.parseInt(radius)) - 1n)
115115
return '0x' + this._state!.nodeRadius.toString(16)
116116
}
117-
case NetworkId.BeaconChainNetwork: {
117+
case NetworkIdByChain[this._client.chainId].BeaconChainNetwork: {
118118
await this._beacon!.setRadius(2n ** BigInt(Number.parseInt(radius)) - 1n)
119119
return '0x' + this._beacon!.nodeRadius.toString(16)
120120
}

packages/portalnetwork/src/client/client.ts

Lines changed: 30 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { EventEmitter } from 'eventemitter3'
88
import packageJson from '../../package.json' with { type: 'json' }
99

1010
import { HistoryNetwork } from '../networks/history/history.js'
11-
import { BeaconNetwork, NetworkId, StateNetwork, SyncStrategy } from '../networks/index.js'
11+
import { NetworkId, type SubNetwork } from '../networks/index.js'
1212
import { PortalNetworkUTP } from '../wire/utp/PortalNetworkUtp/index.js'
1313

1414
import { DBManager } from './dbManager.js'
@@ -24,17 +24,20 @@ import type { IClientInfo } from '../wire/payloadExtensions.js'
2424
import type { Version } from '../wire/types.js'
2525
import { MessageCodes, PortalWireMessageType } from '../wire/types.js'
2626
import { ENRCache } from './enrCache.js'
27-
import type {
28-
INodeAddress,
29-
PortalNetworkEvents,
30-
PortalNetworkMetrics,
31-
PortalNetworkOpts,
27+
import {
28+
ChainId,
29+
type INodeAddress,
30+
type PortalNetworkEvents,
31+
type PortalNetworkMetrics,
32+
type PortalNetworkOpts,
3233
} from './types.js'
34+
import { createNetwork } from '../networks/constructor.js'
3335

3436
export class PortalNetwork extends EventEmitter<PortalNetworkEvents> {
3537
clientInfo: IClientInfo
3638
eventLog: boolean
3739
discv5: Discv5
40+
chainId: ChainId
3841
networks: Map<NetworkId, BaseNetwork>
3942
uTP: PortalNetworkUTP
4043
utpTimout: number
@@ -53,6 +56,7 @@ export class PortalNetwork extends EventEmitter<PortalNetworkEvents> {
5356
*/
5457
constructor(opts: PortalNetworkOpts) {
5558
super()
59+
this.chainId = opts.chainId ?? ChainId.MAINNET
5660
this.clientInfo = {
5761
clientName: 'ultralight',
5862
clientVersionAndShortCommit: `${packageJson.version}-${opts.shortCommit ?? ''}`,
@@ -78,54 +82,21 @@ export class PortalNetwork extends EventEmitter<PortalNetworkEvents> {
7882
)
7983
opts.supportedNetworks = opts.supportedNetworks ?? []
8084
for (const network of opts.supportedNetworks) {
81-
switch (network.networkId) {
82-
case NetworkId.HistoryNetwork:
83-
this.networks.set(
84-
network.networkId,
85-
new HistoryNetwork({
86-
client: this,
87-
networkId: NetworkId.HistoryNetwork,
88-
maxStorage: network.maxStorage,
89-
db: network.db,
90-
gossipCount: opts.gossipCount,
91-
dbSize: async () => opts.dbSize((opts.dataDir ?? '.') + '/history'),
92-
}),
93-
)
94-
break
95-
case NetworkId.StateNetwork:
96-
this.networks.set(
97-
network.networkId,
98-
new StateNetwork({
99-
client: this,
100-
networkId: NetworkId.StateNetwork,
101-
maxStorage: network.maxStorage,
102-
db: network.db,
103-
gossipCount: opts.gossipCount,
104-
dbSize: async () => opts.dbSize((opts.dataDir ?? '.') + '/state'),
105-
}),
106-
)
107-
break
108-
case NetworkId.BeaconChainNetwork:
109-
{
110-
const syncStrategy =
111-
opts.trustedBlockRoot !== undefined
112-
? SyncStrategy.TrustedBlockRoot
113-
: SyncStrategy.PollNetwork
114-
this.networks.set(
115-
network.networkId,
116-
new BeaconNetwork({
117-
client: this,
118-
networkId: NetworkId.BeaconChainNetwork,
119-
maxStorage: network.maxStorage,
120-
trustedBlockRoot: opts.trustedBlockRoot,
121-
sync: syncStrategy,
122-
db: network.db,
123-
gossipCount: opts.gossipCount,
124-
dbSize: async () => opts.dbSize((opts.dataDir ?? '.') + '/beacon'),
125-
}),
126-
)
127-
}
128-
break
85+
try {
86+
const networkInstance = createNetwork(network.networkId, {
87+
client: this,
88+
maxStorage: network.maxStorage,
89+
db: network.db,
90+
gossipCount: opts.gossipCount,
91+
dbSize: (dir: string) => opts.dbSize(dir),
92+
trustedBlockRoot: opts.trustedBlockRoot ? hexToBytes(opts.trustedBlockRoot as `0x${string}`) : undefined,
93+
dataDir: opts.dataDir,
94+
})
95+
this.networks.set(network.networkId, networkInstance)
96+
} catch (err: any) {
97+
this.logger.extend('error')(
98+
`Failed to initialize network ${network.networkId}: ${err.message}`,
99+
)
129100
}
130101
}
131102
for (const network of this.networks.values()) {
@@ -216,25 +187,12 @@ export class PortalNetwork extends EventEmitter<PortalNetworkEvents> {
216187
}
217188
}
218189

219-
public network = (): {
220-
[NetworkId.HistoryNetwork]: HistoryNetwork | undefined
221-
[NetworkId.StateNetwork]: StateNetwork | undefined
222-
[NetworkId.BeaconChainNetwork]: BeaconNetwork | undefined
223-
} => {
224-
const history = this.networks.get(NetworkId.HistoryNetwork)
225-
? (this.networks.get(NetworkId.HistoryNetwork) as HistoryNetwork)
226-
: undefined
227-
const state = this.networks.get(NetworkId.StateNetwork)
228-
? (this.networks.get(NetworkId.StateNetwork) as StateNetwork)
229-
: undefined
230-
const beacon = this.networks.get(NetworkId.BeaconChainNetwork)
231-
? (this.networks.get(NetworkId.BeaconChainNetwork) as BeaconNetwork)
232-
: undefined
233-
return {
234-
[NetworkId.HistoryNetwork]: history,
235-
[NetworkId.StateNetwork]: state,
236-
[NetworkId.BeaconChainNetwork]: beacon,
190+
public network(): Partial<Record<NetworkId, SubNetwork<NetworkId> | undefined>> {
191+
const networks: Partial<Record<NetworkId, SubNetwork<NetworkId> | undefined>> = {}
192+
for (const [networkId, network] of this.networks.entries()) {
193+
networks[networkId] = network as SubNetwork<NetworkId>
237194
}
195+
return networks
238196
}
239197

240198
/**

packages/portalnetwork/src/client/constructor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { type PrefixedHexString, hexToBytes } from '@ethereumjs/util'
44
import { keys } from '@libp2p/crypto'
55
import { multiaddr } from '@multiformats/multiaddr'
66

7-
import { NetworkId } from '../networks/index.js'
7+
import { NetworkIdByChain } from '../networks/index.js'
88
import { RateLimiter } from '../transports/rateLimiter.js'
99
import { MEGABYTE } from '../util/index.js'
1010
import { PortalNetwork } from './client.js'
11-
import { TransportLayer } from './types.js'
11+
import { ChainId, TransportLayer } from './types.js'
1212

1313
import type { IDiscv5CreateOptions, ITransportService, SignableENRInput } from '@chainsafe/discv5'
1414
import type { PortalNetworkOpts } from './types.js'
@@ -137,7 +137,7 @@ export async function createPortalNetwork(
137137
bootnodes,
138138
db: opts.db,
139139
supportedNetworks: opts.supportedNetworks ?? [
140-
{ networkId: NetworkId.HistoryNetwork, maxStorage: 1024 },
140+
{ networkId: NetworkIdByChain[opts.chainId ?? ChainId.MAINNET].HistoryNetwork, maxStorage: 1024 },
141141
],
142142
dbSize: dbSize as () => Promise<number>,
143143
metrics: opts.metrics,

packages/portalnetwork/src/client/eth.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ContentLookup,
1515
HistoryNetworkContentType,
1616
NetworkId,
17+
NetworkIdByChain,
1718
UltralightStateManager,
1819
getContentKey,
1920
reassembleBlock,
@@ -29,19 +30,21 @@ import type {
2930
StateNetwork,
3031
} from '../networks/index.js'
3132
import type { PortalNetwork } from './client.js'
32-
import type { RpcTx } from './types.js'
33+
import type { ChainId, RpcTx } from './types.js'
3334

3435
export class ETH {
36+
chainId: ChainId
3537
history?: HistoryNetwork
3638
state?: StateNetwork
3739
beacon?: BeaconNetwork
3840
activeNetworks: NetworkId[]
3941
logger: Debugger
4042
constructor(portal: PortalNetwork) {
43+
this.chainId = portal.chainId
4144
this.activeNetworks = Array.from(portal.networks.keys())
42-
this.history = portal.network()['0x500b']
43-
this.state = portal.network()['0x500a']
44-
this.beacon = portal.network()['0x500c']
45+
this.history = portal.network()[NetworkIdByChain[portal.chainId].HistoryNetwork] as HistoryNetwork | undefined
46+
this.state = portal.network()[NetworkIdByChain[portal.chainId].StateNetwork] as StateNetwork | undefined
47+
this.beacon = portal.network()[NetworkIdByChain[portal.chainId].BeaconChainNetwork] as BeaconNetwork | undefined
4548
this.logger = portal.logger.extend('ETH')
4649
}
4750

@@ -52,7 +55,7 @@ export class ETH {
5255
* @returns returns the ETH balance of an address at the specified block number or undefined if not available
5356
*/
5457
getBalance = async (address: Uint8Array, blockNumber: bigint): Promise<bigint | undefined> => {
55-
this.networkCheck([NetworkId.StateNetwork, NetworkId.HistoryNetwork])
58+
this.networkCheck([NetworkIdByChain[this.chainId].StateNetwork, NetworkIdByChain[this.chainId].HistoryNetwork])
5659
const stateRoot = await this.history!.getStateRoot(blockNumber)
5760
if (!stateRoot) {
5861
this.logger.extend('getBalance')(`Unable to find StateRoot for block ${blockNumber}`)
@@ -69,7 +72,7 @@ export class ETH {
6972
let header: any
7073
let body: any
7174
let block
72-
this.networkCheck([NetworkId.HistoryNetwork])
75+
this.networkCheck([NetworkIdByChain[this.chainId].HistoryNetwork])
7376
const headerContentKey = getContentKey(HistoryNetworkContentType.BlockHeader, blockHash)
7477
const bodyContentKey = includeTransactions
7578
? getContentKey(HistoryNetworkContentType.BlockBody, blockHash)
@@ -109,7 +112,7 @@ export class ETH {
109112
includeTransactions: boolean,
110113
): Promise<Block | undefined> => {
111114
// Requires beacon light client to be running to get `latest` or `finalized` blocks
112-
this.networkCheck([NetworkId.BeaconChainNetwork])
115+
this.networkCheck([NetworkIdByChain[this.chainId].BeaconChainNetwork])
113116
let clHeader
114117
switch (blockTag) {
115118
case 'latest': {
@@ -135,7 +138,7 @@ export class ETH {
135138
blockNumber: number | bigint | 'latest' | 'finalized',
136139
includeTransactions: boolean,
137140
): Promise<Block | undefined> => {
138-
this.networkCheck([NetworkId.HistoryNetwork])
141+
this.networkCheck([NetworkIdByChain[this.chainId].HistoryNetwork])
139142
if (blockNumber === 'latest' || blockNumber === 'finalized') {
140143
return this.getBlockByTag(blockNumber, includeTransactions)
141144
}
@@ -187,7 +190,7 @@ export class ETH {
187190
* @returns An execution result as defined by the `eth_call` spec
188191
*/
189192
call = async (tx: RpcTx, blockNumber: bigint): Promise<any> => {
190-
this.networkCheck([NetworkId.HistoryNetwork, NetworkId.StateNetwork])
193+
this.networkCheck([NetworkIdByChain[this.chainId].HistoryNetwork, NetworkIdByChain[this.chainId].StateNetwork])
191194
const stateRoot = await this.history!.getStateRoot(blockNumber)
192195
const common = new Common({ chain: Mainnet })
193196
if (!stateRoot) {
@@ -236,7 +239,7 @@ export class ETH {
236239
slot: Uint8Array,
237240
blockTag?: string,
238241
): Promise<string | undefined> {
239-
this.networkCheck([NetworkId.StateNetwork, NetworkId.HistoryNetwork])
242+
this.networkCheck([NetworkIdByChain[this.chainId].StateNetwork, NetworkIdByChain[this.chainId].HistoryNetwork])
240243
if (
241244
blockTag === undefined ||
242245
blockTag === 'pending' ||
@@ -261,7 +264,7 @@ export class ETH {
261264
* @returns
262265
*/
263266
async getTransactionCount(address: Uint8Array, blockTag?: string): Promise<bigint | undefined> {
264-
this.networkCheck([NetworkId.StateNetwork, NetworkId.HistoryNetwork])
267+
this.networkCheck([NetworkIdByChain[this.chainId].StateNetwork, NetworkIdByChain[this.chainId].HistoryNetwork])
265268
if (
266269
blockTag === undefined ||
267270
blockTag === 'pending' ||
@@ -287,7 +290,7 @@ export class ETH {
287290
* @returns code at a given address
288291
*/
289292
async getCode(address: Uint8Array, blockTag?: string) {
290-
this.networkCheck([NetworkId.StateNetwork, NetworkId.HistoryNetwork])
293+
this.networkCheck([NetworkIdByChain[this.chainId].StateNetwork, NetworkIdByChain[this.chainId].HistoryNetwork])
291294
if (
292295
blockTag === undefined ||
293296
blockTag === 'pending' ||

packages/portalnetwork/src/client/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,14 @@ export interface NetworkConfig {
6262
}
6363
}
6464

65+
export enum ChainId {
66+
MAINNET = 'MAINNET',
67+
SEPOLIA = 'SEPOLIA',
68+
ANGELFOOD = 'ANGELFOOD',
69+
}
70+
6571
export interface PortalNetworkOpts {
72+
chainId?: ChainId
6673
shortCommit?: string
6774
operatingSystemAndCpuArchitecture?: string
6875
supportedNetworks?: NetworkConfig[]

0 commit comments

Comments
 (0)