Skip to content

Commit 60ce8e7

Browse files
authored
Repl Tests, Refactoring, and Followup (#3867)
* Fix markdown mistake * Fix condition-check issue * Add repl binary to cli tests * Add tests for generateClientConfig * Throw errors instead of logging and exiting * Merge branch 'master' into repl-followup * Revert "Add repl binary to cli tests" This reverts commit d915867.
1 parent c7d6191 commit 60ce8e7

File tree

5 files changed

+144
-30
lines changed

5 files changed

+144
-30
lines changed

packages/client/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ Additional log selections can be added with a comma separated list (no spaces).
510510
511511
See [DEVELOPER.md](./DEVELOPER.md)
512512
513-
### REPL ###
513+
### REPL
514514
515515
An under-development REPL is now available to users. It can be run using the npm script available in the client package:
516516

packages/client/bin/cli.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,9 @@ async function executeBlocks(client: EthereumClient) {
8787
throw new Error('wrong input')
8888
}
8989
} catch (e: any) {
90-
client.config.logger.error(
90+
throw new Error(
9191
'Wrong input format for block execution, allowed format types: 5, 5-10, 5[0xba4b5fd92a26badad3cad22eb6f7c7e745053739b5f5d1e8a3afb00f8fb2a280,[TX_HASH_2],...], 5[*] (all txs in verbose mode)',
9292
)
93-
process.exit()
9493
}
9594
const { execution } = client.service
9695
if (execution === undefined) throw new Error('executeBlocks requires execution')
@@ -106,15 +105,13 @@ async function startBlock(client: EthereumClient) {
106105
const startBlock = BigInt(args.startBlock)
107106
const height = client.chain.headers.height
108107
if (height < startBlock) {
109-
client.config.logger.error(`Cannot start chain higher than current height ${height}`)
110-
process.exit()
108+
throw new Error(`Cannot start chain higher than current height ${height}`)
111109
}
112110
try {
113111
await client.chain.resetCanonicalHead(startBlock)
114112
client.config.logger.info(`Chain height reset to ${client.chain.headers.height}`)
115113
} catch (err: any) {
116-
client.config.logger.error(`Error setting back chain in startBlock: ${err}`)
117-
process.exit()
114+
throw new Error(`Error setting back chain in startBlock: ${err}`)
118115
}
119116
}
120117

@@ -124,8 +121,7 @@ async function startExecutionFrom(client: EthereumClient) {
124121

125122
const height = client.chain.headers.height
126123
if (height < startExecutionFrom) {
127-
client.config.logger.error(`Cannot start merkle chain higher than current height ${height}`)
128-
process.exit()
124+
throw new Error(`Cannot start merkle chain higher than current height ${height}`)
129125
}
130126

131127
const startExecutionBlock = await client.chain.getBlock(startExecutionFrom)
@@ -147,8 +143,7 @@ async function startExecutionFrom(client: EthereumClient) {
147143
`vmHead set to ${client.chain.headers.height} for starting stateless execution at hardfork=${startExecutionHardfork}`,
148144
)
149145
} catch (err: any) {
150-
client.config.logger.error(`Error setting vmHead for starting stateless execution: ${err}`)
151-
process.exit()
146+
throw new Error(`Error setting vmHead for starting stateless execution: ${err}`)
152147
}
153148
} else if (client.config.statefulVerkle) {
154149
try {
@@ -158,15 +153,13 @@ async function startExecutionFrom(client: EthereumClient) {
158153
`vmHead set to ${client.chain.headers.height} for starting stateful execution at hardfork=${startExecutionHardfork}`,
159154
)
160155
} catch (err: any) {
161-
client.config.logger.error(`Error setting vmHead for starting stateful execution: ${err}`)
162-
process.exit()
156+
throw new Error(`Error setting vmHead for starting stateful execution: ${err}`)
163157
}
164158
} else {
165159
// we need parent state availability to set the vmHead to the parent
166-
client.config.logger.error(
160+
throw new Error(
167161
`Stateful execution reset not implemented at hardfork=${startExecutionHardfork}`,
168162
)
169-
process.exit()
170163
}
171164
}
172165
}

packages/client/bin/utils.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -585,11 +585,9 @@ async function inputAccounts(args: ClientOpts) {
585585
if (address.equals(derivedAddress) === true) {
586586
accounts.push([address, privKey])
587587
} else {
588-
/* eslint-disable no-console */
589-
console.error(
588+
throw new Error(
590589
`Private key does not match for ${address} (address derived: ${derivedAddress})`,
591590
)
592-
process.exit()
593591
}
594592
}
595593
} else {
@@ -599,9 +597,7 @@ async function inputAccounts(args: ClientOpts) {
599597
accounts.push([derivedAddress, privKey])
600598
}
601599
} catch (e: any) {
602-
/* eslint-disable no-console */
603-
console.error(`Encountered error unlocking account:\n${e.message}`)
604-
process.exit()
600+
throw new Error(`Encountered error unlocking account:\n${e.message}`)
605601
}
606602
rl.close()
607603
return accounts
@@ -718,11 +714,7 @@ export async function generateClientConfig(args: ClientOpts) {
718714
customCrypto: cryptoFunctions,
719715
})
720716
} catch (err: any) {
721-
/* eslint-disable no-console */
722-
console.error(err)
723-
console.error(`invalid chain parameters: ${err.message}`)
724-
/* eslint-enable no-console */
725-
process.exit()
717+
throw new Error(`invalid chain parameters: ${err.message}`)
726718
}
727719
} else if (typeof args.gethGenesis === 'string') {
728720
// Use geth genesis parameters file if specified
@@ -736,11 +728,9 @@ export async function generateClientConfig(args: ClientOpts) {
736728
}
737729

738730
if (args.mine === true && accounts.length === 0) {
739-
/* eslint-disable-next-line no-console */
740-
console.error(
731+
throw new Error(
741732
'Please provide an account to mine blocks with `--unlock [address]` or use `--dev` to generate',
742733
)
743-
process.exit()
744734
}
745735

746736
const datadir = args.dataDir ?? Config.DATADIR_DEFAULT

packages/client/src/util/rpc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ function checkHeaderAuth(req: any, jwtSecret: Uint8Array): void {
175175
const token = header.trim().split(' ')[1]
176176
if (!token) throw Error(`Missing jwt token`)
177177
const claims = decode(token.trim(), jwtSecret as never as string, false, algorithm)
178-
const drift = Math.abs(new Date().getTime() - claims.iat * 1000 ?? 0)
178+
const drift = Math.abs(new Date().getTime() - claims.iat * 1000) ?? 0
179179
if (drift > ALLOWED_DRIFT) {
180180
throw Error(`Stale jwt token drift=${drift}, allowed=${ALLOWED_DRIFT}`)
181181
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { keccak256 as keccak256WASM } from '@polkadot/wasm-crypto'
2+
import { keccak256 } from 'ethereum-cryptography/keccak'
3+
import * as fs from 'fs'
4+
import { assert, describe, it } from 'vitest'
5+
6+
import { generateClientConfig } from '../../bin/utils.js'
7+
8+
import type { ClientOpts } from '../../src/types.js'
9+
10+
describe('generateClientConfig', () => {
11+
it('should use chainId over networkId and network name', async () => {
12+
const opts: ClientOpts = {
13+
chainId: 11155111,
14+
networkId: 1,
15+
network: 'mainnet',
16+
}
17+
const { common } = await generateClientConfig(opts)
18+
assert.equal(common.chainId(), 11155111n)
19+
})
20+
21+
it('should fall back to networkId if chainId is not provided', async () => {
22+
const opts: ClientOpts = {
23+
networkId: 11155111,
24+
network: 'mainnet',
25+
}
26+
const { common } = await generateClientConfig(opts)
27+
assert.equal(common.chainId(), 11155111n)
28+
})
29+
30+
it('should fall back to network name if both chainId and networkId are missing', async () => {
31+
const opts: ClientOpts = {
32+
network: 'sepolia',
33+
}
34+
const { common } = await generateClientConfig(opts)
35+
assert.equal(common.chainName(), 'sepolia')
36+
})
37+
38+
it('should initialize WASM crypto when useJsCrypto is false', async () => {
39+
const opts: ClientOpts = {
40+
useJsCrypto: false,
41+
}
42+
const { common } = await generateClientConfig(opts)
43+
assert.deepEqual(
44+
common.customCrypto.keccak256,
45+
keccak256WASM,
46+
'WASM keccak256 should be initialized',
47+
)
48+
})
49+
50+
it('should initialize JS crypto when useJsCrypto is true', async () => {
51+
const opts: ClientOpts = {
52+
useJsCrypto: true,
53+
}
54+
const { common } = await generateClientConfig(opts)
55+
assert.deepEqual(common.customCrypto.keccak256, keccak256, 'JS keccak256 should be initialized')
56+
})
57+
58+
it('should set bootnodes correctly from a file', async () => {
59+
const dir = fs.mkdtempSync('test-bootnodes')
60+
const gethGenesis = `enode://97b85ed04d84f2298f61926eef7ec74a7fff3998f71da1d7eab8c136d8719829150edc2f605b6a741fe6c56af0aa957b0375c38553fd0d0f91c5fa752844c08f@172.16.0.10:30303\nenode://7b58fb9d4fd1bef6fe89b517c4443043294d2650af2bea4c3f7c7f7f1caa40e9c8b22d05d46a77b97867b8d2808c4db351c921a072366a7227ae59a620f6ae39@172.16.0.11:30303\nenode://0257b53da52fa0c141d558d0fc439ea4c338904a762802633daa89fd5770e0cbe306a9e82cc059c4e4293502d5d431e1fa7165f3611840c1bb948d48a7e70b5b@172.16.0.12:30303\nenode://d516e6b38bb8c656bf120fb49d66318952911dcb61e5e950360b7d3ea4ac575228e73cddb7a08a308c6e3f541e284553598f7f5ee3c6c5fe140af350d482aff7@172.16.0.13:30303\nenode://a95a3c8712ff2f09af6a08523164361784fd7ed89e5a68b4f064b3c48fde73646f63296bab822a2133f1cbc73d9aa9726b9374f03ca66b7cbb721779cc6f5a87@172.16.0.14:30303`
61+
fs.open(`${dir}/bootnodes.txt`, 'w', (err, fd) => {
62+
if (err !== null) throw err
63+
fs.write(fd, gethGenesis, (writeErr) => {
64+
if (writeErr !== null) {
65+
assert.fail(`Error writing the file: ${writeErr.message}`)
66+
} else {
67+
assert.ok(true, 'File created and data written successfully!')
68+
}
69+
70+
fs.close(fd, (closeErr) => {
71+
if (closeErr) {
72+
assert.fail(`Error closing the file:, ${closeErr.message}`)
73+
}
74+
})
75+
})
76+
})
77+
const opts: ClientOpts = {
78+
bootnodes: [`./${dir}/bootnodes.txt`],
79+
}
80+
const { config } = await generateClientConfig(opts)
81+
assert.ok(Array.isArray(config.bootnodes), 'Bootnodes should be an array')
82+
})
83+
84+
it('should require an unlocked account when mining', async () => {
85+
const opts: ClientOpts = {
86+
mine: true,
87+
}
88+
try {
89+
await generateClientConfig(opts)
90+
assert.fail('Expected generateClientConfig to throw error when mining without an account')
91+
} catch (err: any) {
92+
assert.match(err.message, /Please provide an account to mine blocks/)
93+
}
94+
})
95+
96+
it('should enable mining when mine=true', async () => {
97+
const opts: ClientOpts = {
98+
mine: true,
99+
dev: true,
100+
}
101+
const { config } = await generateClientConfig(opts)
102+
assert.ok(config.mine, 'Mining should be enabled')
103+
})
104+
105+
it('should properly configure Prometheus when enabled', async () => {
106+
const opts: ClientOpts = {
107+
prometheus: true,
108+
prometheusPort: 9090,
109+
}
110+
const { metricsServer } = await generateClientConfig(opts)
111+
assert.ok(metricsServer, 'Prometheus should be enabled and metrics server should be started')
112+
})
113+
114+
it('should correctly handle dev mode initialization', async () => {
115+
const opts: ClientOpts = {
116+
dev: true,
117+
dataDir: './test-data',
118+
}
119+
const { config } = await generateClientConfig(opts)
120+
assert.ok(config.mine, 'Mining should be enabled in dev mode')
121+
assert.ok(config.isSingleNode, 'Single node mode should be enabled')
122+
})
123+
124+
it('should properly set logging options', async () => {
125+
const opts: ClientOpts = {
126+
logLevel: 'debug',
127+
}
128+
const { config } = await generateClientConfig(opts)
129+
assert.equal(config.logger.level, 'debug', 'Log level should be set to debug')
130+
})
131+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy