ethereumjs-monorepo

@ethereumjs/client

NPM Package GitHub Issues Actions Status Code Coverage Discord

| Ethereum Execution Layer (EL) Client built in TypeScript/JavaScript. | | ——————————————————————– |

Table of Contents

Introduction

The EthereumJS Client is an Ethereum Execution Client (similar to go-ethereum or Nethermind) written in TypeScript/JavaScript, the non-Smart-Contract language Ethereum dApp developers are most familiar with. It is targeted to be a client for research and development and not meant to be used in production on mainnet for the foreseeable future (out of resource and security considerations).

Here are some use cases:

The client has an extremely modular design by building upon central other libraries in the EthereumJS monorepo (VM, Merkle Patricia Tree, Blockchain, Block, tx, devp2p and Common) and is therefore extremely well suited for a deep dive into Ethereum protocol development.

We invite you to explore and would be delighted if you give us feedback on your journey! 🙂 ❤️

Installation

To be able to run the EthereumJS client, you need a working Node.js installation, see e.g. these docs from the npm documentation for combined instructions on how to install Node.js and eventually npm.

We currently recommend to run the client with a recent Node.js version 18 installation.

NPM Installation

Client releases are done on a regular basis on npm.

You can install the latest version with:

npm install -g @ethereumjs/client

Source Installation

The client can also be easily installed and built from source:

  1. Clone the EthereumJS monorepo with git clone https://github.com/ethereumjs/ethereumjs-monorepo.git
  2. Run npm i from the root folder to install dependencies and build
  3. Now the client can be run from the packages/client folder with npm run client:start
  4. Run npm run client:start -- --help for help on the CLI parameters

Docker

A Docker image is built nightly from the current master branch and can be retrieved via the below command:

docker pull ethpandaops/ethereumjs:master

Alternatively, an image from the most recent stable release can be accessed via:

docker pull ethpandaops/ethereumjs:stable

You can also build your own image by going to the repository root directory and run:

docker build . -f Dockerfile --tag ethereumjs:latest

You may now do appropriate directory/file mounts for data dir and jwtsecret file and provide their path appropriately in the client run command.

Also, in your docker run command, be sure to include the --init flag so that the container will properly handle kernel signals (like SIGINT and SIGTERM). Not doing so can lead to unexpected behaviour (as documented here)

General Usage

You can get the client up and running by going to the shell and run:

# npm installation
ethereumjs

# Source installation
npm run client:start

And pass in CLI parameters like:

# npm installation
ethereumjs --network=holesky

# Source installation
npm run client:start -- --network=holesky

To see a help page with the full list of client options available run:

ethereumjs --help

For the networks that have transitioned to PoS aka merged (all Ethereum networks have been merged), checkout how to run Ethereumjs in the PoS configuration.

Supported Networks

The EthereumJS client is tightly integrated with the EthereumJS Common library and gets its network state and information from this library. The client supports all networks supported by Common.

The main supported networks are:

Use the CLI --network option to switch the network:

ethereumjs --network=holesky

The client currently supports full sync being set as a default and has experimental support for light sync.

Running with a Consensus Layer (CL) Client

In most scenarios you will want to run the EthereumJS client in a combination with a consensus layer (CL) client. The most tested combination is to run the client with the Lodestar TypeScript CL client. Lodestar provides a quick-start repository that allows users to get started quickly with minimal configuration. After cloning the linked quick-start repository, all that should be necessary to get the Lodestar consensus client started with EthereumJS is to run the following command:

./setup.sh --network sepolia --dataDir sepolia-data --elClient ethereumjs

Other possible options are to run with Prysm (Go), Lighthouse (Rust), Nimbus (Nim) or Teku (Java).

Necessary CLI Options

Engine API

Execution and consensus layer clients communicate with each other through a new set of JSON-RPC API calls, the so-called Engine API.

The Engine API can be activated in the client with:

--rpcEngine

This will expose engine endpoints at 8551(default port, can be modified see cli help via --help).

JWT Authentication

To ensure a secure communication channel between clients, a JWT token is needed for authentication. If no further option is provided in the client, a JWT token is created on first run and stored to a predefined location (see CLI output). This secret will then be re-used by default, and can then be provided to the CL client (see CL client documentation for details).

To use an existing token the path to the token can be passed to the client with the following flag:

--jwt-secret

JWT authentication can be disabled by adding the --rpcEngineAuth false flag (do not use in production).

Example: Setup with Lodestar

Beacon

The following is a rough guidance to run Lodestar as a beacon (non-validating) Node, see Lodestar docs for more complete and up-to-date instructions on beacon management with Lodestar.

  1. Use lodestar branch stable and run yarn && yarn build
  2. Run cmd: ./lodestar beacon --network holesky --jwt-secret /path/to/jwtsecret/file

This will by default try connecting to ethereumjs over the endpoint 8551. (You may customize this in conjunction with ethereumjs, see lodestar cli help via --help).

You may provide --checkpointSyncUrl (with a synced holesky beacon node endpoint as arg value) to start directly off the head/provided checkpoint on the holesky beacon chain, possibly triggering (backfill) beacon sync on ethereumjs.

(Optional) Validator

Once you start a beacon node as instructed above, it will expose some default api endpoints. The validator started as instructed below will connect to the beacon node via these apis (Refer to lodestar cli help to modify the default endpoints for both beacon and validator). Again more complete and up-to-date docs are available within the Lodestar documentation.

  1. Run cmd: ./lodestar validator --importKeystores /path/to/generated/keystores --importKeystoresPassword /path/to/keystores/password/file

where the keystores have been generated via staking-deposit-cli with the same password as in the above provided password file.

For a testnet chain, you may skip keystore generation and directly provide lodestar validator with a mnemonic and its range indices to derive validators via --fromMnemonic and --mnemonicIndexes args. For e.g.:

./lodestar validator --fromMnemonic "lens risk clerk foot verb planet drill roof boost aim salt omit celery tube list permit motor obvious flash demise churn hold wave hollow" --mnemonicIndexes 0..5

(Modify the mnemonic and range indices as per your validator configuration).

Running EthereumJS/Lodestar on Holesky

A suited network to test the EthereumJS/Lodestar client combination is the Holesky network, being still somewhat lightweight but nevertheless being actively used with a significant transaction load.

To start the EthereumJS client with JSON RPC and Engine API endpoints exposed:

ethereumjs --network=holesky --rpc --rpcEngine

Then start the Lodestar client with:

./lodestar beacon --network=holesky --jwt-secret=[PATH-TO-JWT-SECRET-FROM-ETHEREUMJS-CLIENT]

Experimental Testnets

The EthereumJS client supports ongoing protocol development efforts, allowing developers and testers to participate in various testnets using the EthereumJS client.

Stateless Verkle

We currently support the Verkle Kaustinen6 testnet, and will be proactively supporting upcoming testnets (e.g. Kaustinen7) as they launch. To start the EthereumJS client alongside the Lodestar consensus client, use the following commands:

Step 1 - Running the EthereumJS client (from the cloned @ethereumjs/client package)

npm run client:start:ts -- --dataDir /data/k6data --network kaustinen6 --rpcEngine --rpcEngineAuth false --logLevel warn

Step 2 - Running the Lodestar client (from the cloned Lodestar quick-start repository)

setup.sh --dataDir k6data --network kaustinen6 --justCL

Additional information on the Kaustinen6 testnet can be retrieve from this page: https://verkle-gen-devnet-6.ethpandaops.io/

The process should be similar for other testnets, and the quick-start repository should provide testnet-specific configuration instructions for the Lodestar consensus layer client.

Custom Chains

The EthereumJS client supports running custom chains based on a custom chain configuration. There are two ways of reading in custom chain configuration parameters:

Common-based Configuration

We have got our own flexible chain configuration and genesis state configuration format applied in the Common library, see the Common chain JSON files as well as corresponding blockchain Genesis JSON files for inspiration.

Custom chain files following this format can be passed in to the client with the following options:

ethereumjs --customChain=[COMMON_FORMAT_CHAIN_FILE] --customGenesisState=[COMMON_FORMAT_GENESIS_STATE]

Geth Genesis Files

It is also possible to use a chain configuration as defined by the Go-Ethereum genesis.json file format.

Use the following option to pass in to the client:

ethereumjs --gethGenesis=[GETH_GENESIS_JSON_FILE]

Custom Network Mining (Beta)

The client supports private custom network mining by using the --mine option together with passing in a comma separated list of accounts with the --unlock option:

ethereumjs --mine --unlock=[ADDRESS1],[ADDRESS2],...

Note that this feature is in beta and shouldn’t be used with accounts holding a substantial amount of Ether on mainnet (or other valuable assets) for security reasons.

Custom Network for Development

The client provides a quick way to get a local instance of a blockchain up and running using the --dev command. This will start up a private PoA clique network with a prefunded account that mines block on 10 second intervals. The prefunded account and its private key are printed to the screen when the client starts. When paired with the --rpc command, you have a ready-made environment for local development.

ethereumjs --dev=poa --rpc

==================================================
Account generated for mining blocks:
Address: 0xd8066d5822138e7c76d1565deb249f5f7ae370fa
Private key: 0x6239e36ab8b27212868a1aa3f9c3b88b084075ea56aa4979d206371f065d3feb
WARNING: Do not use this account for mainnet funds
==================================================

Please heed the warning and do not use the provided account/private key for mainnet funds.

This can also be paired with the --unlock command if you would like to specify the miner/prefunded account:

ethereumjs --dev=poa --rpc --unlock=0xd8066d5822138e7c76d1565deb249f5f7ae370fa

Note: If the --dev command is used in conjunction with --unlock to use a predefined account, the blockchain’s state will be preserved between consecutive runs. If you try to use a different predefined account, you may see errors related to incompatible genesis blocks. Simply run the client with the --dev=poa flag by itself and use the new prefunded account provided by the client in further rounds of execution.

To explicitly set the miner coinbase (etherbase) specify --minerCoinbase=[ADDRESS] - otherwise this will default to the primary account.

The --dev command must be passed with a value (either ‘poa’ or ‘pow’) or you will receive an error.

CLI Parameter Autocompletion (experimental)

The client supports a primitive form of autocompletion for CLI parameters. To enable, first ensure you have built the client (or installed from NPM).

Then, configure the client to use autocompletion. This works with either bash or zsh shells.

Set up the autocompletion script.

dist/bin/cli.js completion >> ~/.zshrc

Close and reopen the your terminal window (or else run source ~/.zshrc). When you next type dist/bin/cli.js --d and then press tab, you should see something like:

dist/bin/cli.js --d
--dataDir            -- Data directory for the blockchain
--debugCode          -- Generate code for local debugging (internal usage mostly)
--dev                -- Start an ephemeral PoA blockchain with a single miner and prefunded accounts
--disableBeaconSync  -- Disables beacon (optimistic) sync if the CL provides blocks at the head of the chain
--discDns            -- Query EIP-1459 DNS TXT records for peer discovery
--discV4             -- Use v4 ("findneighbour" node requests) for peer discovery
--dnsAddr            -- IPv4 address of DNS server to use when acquiring peer discovery targets
--dnsNetworks        -- EIP-1459 ENR tree urls to query for peer discovery targets

Metrics

The client can optionally collect metrics using the Prometheus metrics platform and expose them via an HTTP endpoint with the following CLI flags.
The current metrics that are reported by the client can be found at the default port and route: localhost:8000/metrics.

# npm installation
ethereumjs --prometheus

# source installation
npm run client:start:ts -- --prometheus --prometheusPort=9123

API

API Reference

See also this diagram for an overview of the client structure with the initialization and message flow.

JSON-RPC

Overview

You can expose a JSON-RPC interface along a client run with:

ethereumjs --rpc

To run just the server without syncing:

ethereumjs --rpc --maxPeers=0

Currently only a small subset of RPC methods are implemented.(*) You can have a look at the ./src/rpc/modules/ source folder or the tracking issue #1114 for an overview.

(*) Side note: implementing RPC methods is actually an extremely thankful task for a first-time contribution on the project *hint* hint. 😄

API Examples

You can use cURL to request data from an API endpoint. Here is a simple example for web3_clientVersion:

curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","id":1,"method":"web3_clientVersion", "params": []}' http://localhost:8545

Note that "params": [] can also be omitted in this case.

Or - somewhat more convenient and with formatted output - with a tool like httpie:

http POST http://localhost:8545 jsonrpc=2.0 id=1 method=web3_clientVersion params:=[]

Note the := separator for the params parameter to indicate raw JSON as an input.

This will give you an output like the following:

{
  "id": "1",
  "jsonrpc": "2.0",
  "result": "EthereumJS/0.0.5/darwin/node12.15.0"
}

Here’s an example for a call on an endpoint with the need for parameters. The following call uses the eth_getBlockByNumber endpoint to request data for block number 436 (you can use a tool like RapidTables for conversion to hex):

curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1b4", true],"id":1}' http://127.0.0.1:8545

Same with httpie:

http POST http://localhost:8545 jsonrpc=2.0 id=1 method=eth_getBlockByNumber params:='["0x1b4",true]'

Output:

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "number": "0x1b4",
    "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae",
    "parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54",
    "nonce": "0x689056015818adbe",
    "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
    "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d",
    "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "miner": "0xbb7b8287f3f0a933474a79eae42cbca977791171",
    "difficulty": "0x4ea3f27bc",
    "totalDifficulty": "0x78ed983323d",
    "extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32",
    "size": "0x747",
    "gasLimit": "0x1388",
    "gasUsed": "0x0",
    "timestamp": "0x55ba467c",
    "transactions": [],
    "uncles": []
  }
}

Development

See also DEVELOPER.md.

Design

For an overview on the design goals which served as a guideline on design decisions as well as some structural client overview see the dedicated DESIGN.md document.

Client Customization

To get a start on customizing the client and using it programmatically see the code from ./bin/cli.ts to get an idea of how an EthereumClient instance is invoked programmatically.

We would love to hear feedback from you on what you are planning and exchange on ideas how a programmatic exposure of the client API can be achieved more systematically and useful for third-party development use.

Debugging

Local Test Network

For some guidance on how to setup local testnetworks see the examples on local debugging and setting up a private network with Geth.

Using Debug Loggers

The client’s logging verbosity level can be set with --loglevel. Available levels are error, warn, info, debug.

ethereumjs --loglevel=debug

For more in-depth debugging on networking the underlying devp2p library integrates with the debug package and can also be used from within a client execution context:

DEBUG=ethjs,*,-babel [CLIENT_START_COMMAND]

The above command outputs the log messages from all devp2p debug loggers available. For more targeted logging, the different loggers can also be activated separately, e.g.:

DEBUG=ethjs,devp2p:rlpx,devp2p:eth,-babel [CLIENT_START_COMMAND]

The following options are available:

| Logger              | Description                                      |
| ------------------- | ------------------------------------------------ |
| `client:fetcher:#`            | This option enables logging for core fetcher operations such as job scheduling   |
| `client:fetcher:bytecode`   | This option enables logging for the snap sync bytecode fetcher         |
| `client:fetcher:storage`    | This option enables logging for the snap sync storage fetcher  |
| `client:fetcher:trienode`   | This option enables logging for the snap sync trienode fetcher      |
| `client:fetcher:account`    | This option enables logging for the snap sync account fetcher      |

ethjs must be included in the DEBUG environment variables to enable any logs. Additional log selections can be added with a comma separated list (no spaces). Logs with extensions can be enabled with a colon :, and * can be used to include all extensions.

DEBUG=ethjs,client:fetcher:*,devp2p:eth npm run client:start

Hive testing

See DEVELOPER.md

Diagram Updates

To update the structure diagram files in the root folder open the client.drawio file in draw.io, make your changes, and open a PR with the updated files. Export svg and png with border width=20 and transparency=false. For png go to “Advanced” and select 300 DPI.

EthereumJS

See our organizational documentation for an introduction to EthereumJS as well as information on current standards and best practices.

If you want to join for work or do improvements on the libraries have a look at our contribution guidelines.