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.4.0 as our beta version geth.
cd morph
git checkout v0.4.0
Build Geth
Notice: You need C compiler to build geth
make geth
Build Node
cd ~/.morph/morph/node
make build
Sync from the genesis block
Config Preparation
- Download the config files and make data dir
cd ~/.morph
## mainnet
wget https://raw.githubusercontent.com/morph-l2/config-template/main/mainnet/data.zip
## testnet
wget https://raw.githubusercontent.com/morph-l2/config-template/main/holesky/data.zip
unzip data.zip
- Create a shared secret with node
cd ~/.morph
openssl rand -hex 32 > jwt-secret.txt
Script to start the process
Geth
./morph/go-ethereum/build/bin/geth --morph \
--datadir "./geth-data" \
--http --http.api=web3,debug,eth,txpool,net,engine \
--authrpc.addr localhost \
--authrpc.vhosts="localhost" \
--authrpc.port 8551 \
--authrpc.jwtsecret=./jwt-secret.txt \
--log.filename=./geth.log
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
## mainnet
export CHAIN_ID=1
export L1MESSAGEQUEUE_CONTRACT=0x3931ade842f5bb8763164bdd81e5361dce6cc1ef
export START_HEIGHT=20996776
export ROLLUP=0x759894ced0e6af42c26668076ffa84d02e3cef60
## start node
./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 RPC) \
--l1.beaconrpc $(Ethereum beacon chain RPC) \
--l1.chain-id ${CHAIN_ID} \
--validator.privateKey 0x0000000000000000000000000000000000000000000000000000000000000001 \
--sync.depositContractAddr ${L1MESSAGEQUEUE_CONTRACT} \
--sync.startHeight ${START_HEIGHT} \
--derivation.rollupAddress ${ROLLUP} \
--derivation.startHeight ${START_HEIGHT} \
--derivation.fetchBlockRange 200 \
--log.filename ./node.log
For holesky network, using
export CHAIN_ID=17000
export L1MESSAGEQUEUECONTRACT=0x778d1d9a4d8b6b9ade36d967a9ac19455ec3fd0b
export START_HEIGHT=1434640
export ROLLUP=0xd8c5c541d56f59d65cf775de928ccf4a47d4985c
Note the validator.privateKey is of no use to you. It is used to send challenges when the state root is found to be incorrect. However, we do not currently accept challenges from third party addresses. But it is also a required parameter for the morphnode command, so we give a 0x00... 1.
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.