Skip to main content

How to Run a Morph Validator Node

Run a Morph Node

This guide describes the approach to running a Morph validator node. If you are unfamiliar with the validator duties, please refer to our optimistic zkEVM design.

Create the folder ~/.morph as our home directory for this example.

Build executable binary

Clone Morph

mkdir -p ~/.morph 
cd ~/.morph
git clone https://github.com/morph-l2/morph.git

Currently, we use tag v0.2.0-beta as our beta version geth.

cd morph
git checkout v0.2.0-beta

Build Geth

Notice: You need C compiler to build geth

make nccc_geth

Build Node

cd ~/.morph/morph/node 
make build

Sync from the genesis block

Config Preparation

  1. Download the config files and make data dir
cd ~/.morph
wget https://raw.githubusercontent.com/morph-l2/config-template/main/holesky/data.zip
unzip data.zip
  1. Create a shared secret with node
cd ~/.morph
openssl rand -hex 32 > jwt-secret.txt

Script to start the process

Geth


NETWORK_ID=2810

nohup ./morph/go-ethereum/build/bin/geth \
--datadir=./geth-data \
--verbosity=3 \
--http \
--http.corsdomain="*" \
--http.vhosts="*" \
--http.addr=0.0.0.0 \
--http.port=8545 \
--http.api=web3,eth,txpool,net,engine \
--ws \
--ws.addr=0.0.0.0 \
--ws.port=8546 \
--ws.origins="*" \
--ws.api=web3,eth,txpool,net,engine \
--networkid=$NETWORK_ID \
--authrpc.addr="0.0.0.0" \
--authrpc.port="8551" \
--authrpc.vhosts="*" \
--authrpc.jwtsecret=$JWT_SECRET_PATH \
--gcmode=archive \
--metrics \
--metrics.addr=0.0.0.0 \
--metrics.port=6060 \
--miner.gasprice="100000000"

tail -f geth.log to check if the Geth is running properly, or you can also execute the below curl command to check if you are connected to the peer.

curl --location --request POST 'localhost:8545/' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_blockNumber",
"id":1
}'

{"jsonrpc":"2.0","id":1,"result":"0x148e39"}

Node

cd ~/.morph
export L1MessageQueueWithGasPriceOracle=0x778d1d9a4d8b6b9ade36d967a9ac19455ec3fd0b
export START_HEIGHT=1434640
export Rollup=0xd8c5c541d56f59d65cf775de928ccf4a47d4985c
./morph/node/build/bin/morphnode --validator --home ./node-data \
--l2.jwt-secret ./jwt-secret.txt \
--l2.eth http://localhost:8545 \
--l2.engine http://localhost:8551 \
--l1.rpc $(Ethereum Holesky RPC) \
--l1.beaconrpc $(Ethereum Holesky beacon chain RPC) \
--l1.chain-id 17000 \
--validator.privateKey $(Your Validator Key) \
--sync.depositContractAddr $(L1MessageQueueWithGasPriceOracle) \
--sync.startHeight $(START_HEIGHT) \
--derivation.rollupAddress $(Rollup) \
--derivation.startHeight $(START_HEIGHT) \
--derivation.fetchBlockRange 200 \
--log.filename ./node.log

Check Status

If your node is successfully started, you will see the following response:

I[2024-06-06|15:57:35.216] metrics server enabled                       module=derivation host=0.0.0.0 port=26660
derivation node starting
ID> 24-06-06|15:57:35.216] initial sync start module=syncer msg="Running initial sync of L1 messages before starting sequencer, this might take a while..."
I[2024-06-06|15:57:35.242] initial sync completed module=syncer latestSyncedBlock=1681622
I[2024-06-06|15:57:35.242] derivation start pull rollupData form l1 module=derivation startBlock=1681599 end=1681622
I[2024-06-06|15:57:35.244] fetched rollup tx module=derivation txNum=8 latestBatchIndex=59201
I[2024-06-06|15:57:35.315] fetch rollup transaction success module=derivation txNonce=8764 txHash=0x5fb8a98472d1be73be2bc6be0807b9e0c68b7ba14a648c8a17bdaff7b26eb923 l1BlockNumber=1681599 firstL2BlockNumber=1347115 lastL2BlockNumber=1347129
I[2024-06-06|15:57:35.669] new l2 block success module=derivation blockNumber=1347115

You can use the following command to check the newest block height to make sure you are aligned.

curl --location --request POST 'localhost:8545/' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc":"2.0",
"method":"eth_blockNumber",
"id":1
}'
{"jsonrpc":"2.0","id":1,"result":"0x148e39"}

Make sure you check the validator status constantly, if you find response

[2024-06-14|16:43:50.904] root hash or withdrawal hash is not equal    originStateRootHash=0x13f91d1c272e48e2d864ce7bfb421506d5b2a04def64d45c75391cdcdd69cd78 deriveStateRootHash=0x27e10420c0e34676a7d75c4189d7ccd1c3407cc8fd0b3eafb01c15e250a1215f batchWithdrawalRoot=0xa3e4a7cf45c7591a6bd9868f1fa7485ae345f10067acaade5f5b07d418b2e172 deriveWithdrawalRoot=0xa3e4a7cf45c7591a6bd9868f1fa7485ae345f10067acaade5f5b07d418b2e172

This means your validators find inconsistent between sequencer submission and your own observation.