ethereumjs / ethereumjs-monorepo

Monorepo for the Ethereum VM TypeScript Implementation
2.6k stars 756 forks source link

Run `inlineClient` in REPL #3776

Open acolytec3 opened 1 week ago

acolytec3 commented 1 week ago

One thing that might make troubleshooting the client on devnets and the like easier would be to be able to run it in a REPL so we can have direct access to the client object as its running, similarly to how geth has their javascript console.

We already have a createInlineClient function available to us that accepts the entire configuration object available to the client so it shouldn't be too hard to expand this a bit to allow a user to pass in a geth genesis.json and start a client entirely within a REPL.

The piece that isn't built yet would be hooking it up to the RPC. We have a startRPCServers function that we could pass the instantiated "inline" client to and then just grab a reference to it in the REPL so we can be sure to not lose it.

This could also be an interesting "community showcase" idea.

acolytec3 commented 6 days ago

This script is a working proof of concept.

const { readFileSync } = await import('fs')

const { createCommonFromGethGenesis, parseGethGenesis } = await import('@ethereumjs/common')
const { createInlineClient } = await import('./test/sim/simutils.ts')
const { Config } = await import('./src/config.ts')
const { getLogger } = await import('./src/logging.ts')
const { startRPCServers } = await import('./bin/startRPC.ts')
const genesisFile = JSON.parse(readFileSync('./genesis.json', 'utf-8'))
const chainName = 'pectra'
const common = createCommonFromGethGenesis(genesisFile, {
  chain: chainName,
})
const config = new Config({ common, logger: getLogger({ logLevel: 'debug' }), saveReceipts: true, enableSnapSync: true })
const client = await createInlineClient(config, common, {}, '', true)
const servers = startRPCServers(client, {
  rpc: true,
  rpcAddr: '0.0.0.0',
  rpcPort: 8545,
  ws: false,
  wsPort: 0,
  wsAddr: '0.0.0.0',
  rpcEngine: true,
  rpcEngineAddr: '0.0.0.0',
  rpcEnginePort: 8551,
  wsEngineAddr: '0.0.0.0',
  wsEnginePort: 8552,
  rpcDebug: 'eth',
  rpcDebugVerbose: 'false',
  helpRPC: false,
  jwtSecret: '',
  rpcEngineAuth: false,
  rpcCors: '',
})

To see in action: 1) Copy the above snippet into script.ts 2) Run npx tsx in the same directory where script.ts is 3) Type .load script.ts (this runs the script as though you were typing it in directly)

Behold your inline client.

[Uploading Screencast from 10-29-2024 04:01:37 PM.webm…]()

acolytec3 commented 6 days ago

And if you want to create an actual custom javascript console for our client, here's another variant.

import repl from 'repl'

const setupClient = async () => {
  const { readFileSync } = await import('fs')

  const { createCommonFromGethGenesis, parseGethGenesis } = await import('@ethereumjs/common')
  const { createInlineClient } = await import('./test/sim/simutils.ts')
  const { Config } = await import('./src/config.ts')
  const { getLogger } = await import('./src/logging.ts')
  const { startRPCServers } = await import('./bin/startRPC.ts')
  const genesisFile = JSON.parse(readFileSync('./genesis.json', 'utf-8'))
  const chainName = 'pectra'
  const common = createCommonFromGethGenesis(genesisFile, {
    chain: chainName,
  })
  const config = new Config({
    common,
    logger: getLogger({ logLevel: 'info' }),
    saveReceipts: true,
    enableSnapSync: true,
  })
  const client = await createInlineClient(config, common, {}, '', true)
  const servers = startRPCServers(client, {
    rpc: true,
    rpcAddr: '0.0.0.0',
    rpcPort: 8545,
    ws: false,
    wsPort: 0,
    wsAddr: '0.0.0.0',
    rpcEngine: true,
    rpcEngineAddr: '0.0.0.0',
    rpcEnginePort: 8551,
    wsEngineAddr: '0.0.0.0',
    wsEnginePort: 8552,
    rpcDebug: 'eth',
    rpcDebugVerbose: 'false',
    helpRPC: false,
    jwtSecret: '',
    rpcEngineAuth: false,
    rpcCors: '',
  })
  return client
}

const client = await setupClient()

const replServer = repl.start({
  prompt: 'EthJS > ',
  ignoreUndefined: true,
})

replServer.context.client = client

console.log('Custom console started. Type .help for available commands.')

Just run npx tsx script.ts and away we go.

holgerd77 commented 6 days ago

Interesting. 👀

Thanks for the write up, super helpful! 🙂🙏

holgerd77 commented 6 days ago

(guess a lot of API work still to be done though…)