Deploy a rpc only node
Deployment Overview
Current Testnet and Mainnet version: fork9.
Recommended configuration for mainnet, can appropriately downgrade for testnet.
For mainnet: Ensure Docker's home directory is set to the maximum available disk space and configure disk usage alerts.
For mainnet: Choose the Singapore region as the deployment location for your synchronization nodes.
Please ensure that time synchronization is enabled and set to UTC+0 on all deployment-related servers.
Database
Sync Server
state-db, prover-db
sync, prover
CPU: 64VCORE, >= 3050MHZ
RAM: 128G
DISK: 2000GB, NVMe SSD
Synchronize the L2 state from master node and L1, only one can run at a time.
RPC Server(s)
json-rpc, prover
CPU: 64VCORE, >= 3050MHZ
RAM: 128G
DISK: 2000GB, NVMe SSD
Provide access entry for external requests, can run multiple instances.
Deployment Steps
Prepare configuration files
docker-compose.yml
If you have modified the default configuration of the database container [merlin-state-db], please synchronize this configuration with [StateDB] in node.config.toml and databaseURL in prover.config.json.
version: "3.5"
networks:
default:
name: merlin-rpc-network
services:
cdk-validium-state-db:
container_name: merlin-state-db
restart: unless-stopped
image: postgres:15
cpu_shares: 4096
ports:
- 5432:5432
environment:
- POSTGRES_USER=state_user
- POSTGRES_PASSWORD=state_password
- POSTGRES_DB=state_db
volumes:
- ./init_prover_db.sql:/docker-entrypoint-initdb.d/init.sql
- /data/merlin/data/statedb:/var/lib/postgresql/data
command:
- "postgres"
- "-N"
- "500"
- "-c"
- "max_connections=20000"
- "-c"
- "shared_buffers=16GB"
- "-c"
- "work_mem=32MB"
- "-c"
- "wal_buffers=8MB"
- "-c"
- "effective_cache_size=16GB"
- "-c"
- "maintenance_work_mem=128MB"
- "-c"
- "checkpoint_completion_target=0.7"
- "-c"
- "max_wal_size=4GB"
- "-c"
- "min_wal_size=200MB"
- "-c"
- "max_worker_processes=16"
cdk-validium-pool-db:
container_name: merlin-pool-db
restart: unless-stopped
image: postgres:15
cpu_shares: 4096
ports:
- 5433:5432
environment:
- POSTGRES_USER=pool_user
- POSTGRES_PASSWORD=pool_password
- POSTGRES_DB=pool_db
volumes:
- /data/merlin/data/pooldb:/var/lib/postgresql/data
command:
- "postgres"
- "-N"
- "500"
- "-c"
- "max_connections=10000"
cdk-validium-prover:
container_name: merlin-prover
image: hermeznetwork/zkevm-prover:v6.0.2
restart: unless-stopped
logging:
options:
max-size: '500m'
max-file: '3'
ports:
- 50061:50061 # MT
- 50071:50071 # Executor
volumes:
- ./prover.config.json:/usr/src/app/config.json
command: >
zkProver -c /usr/src/app/config.json
cdk-validium-sync:
container_name: merlin-sync
image: merlinadmin/zkevm-node:v3.1.0
restart: unless-stopped
logging:
options:
max-size: '500m'
max-file: '3'
ports:
- 9091:9091
volumes:
- ./node.config.toml:/app/config.toml
- ./genesis.json:/app/genesis.json
command:
- "/bin/sh"
- "-c"
- "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components synchronizer"
cdk-validium-json-rpc:
container_name: merlin-rpc
image: merlinadmin/zkevm-node:v3.1.0
restart: unless-stopped
logging:
options:
max-size: '500m'
max-file: '5'
ports:
- 8123:8123
- 8133:8133 # needed if WebSockets enabled
- 9092:9091 # needed if metrics enabled
volumes:
- ./node.config.toml:/app/config.toml
- ./genesis.json:/app/genesis.json
command:
- "/bin/sh"
- "-c"
- "/app/zkevm-node run --network custom --custom-network-file /app/genesis.json --cfg /app/config.toml --components rpc --http.api eth,net,debug,zkevm,txpool,web3" #with debug mode. delete "--http.api eth,net,debug,zkevm,txpool,web3" will remove debug endpoint.
init_prover_db.sql
CREATE DATABASE prover_db;
\connect prover_db;
CREATE SCHEMA state;
CREATE TABLE state.nodes (hash BYTEA PRIMARY KEY, data BYTEA NOT NULL);
CREATE TABLE state.program (hash BYTEA PRIMARY KEY, data BYTEA NOT NULL);
CREATE USER prover_user with password 'prover_password';
ALTER DATABASE prover_db OWNER TO prover_user;
ALTER SCHEMA state OWNER TO prover_user;
ALTER SCHEMA public OWNER TO prover_user;
ALTER TABLE state.nodes OWNER TO prover_user;
ALTER TABLE state.program OWNER TO prover_user;
ALTER USER prover_user SET SEARCH_PATH=state;
node.config.toml
Update [StateDB] if you use a customized database configuration.
IsTrustedSequencer = false
[Log]
Environment = "production" # "production" or "development"
Level = "info"
Outputs = ["stderr"]
[Synchronizer]
SyncInterval = "1s"
SyncChunkSize = 300
SyncOnlyTrusted = true
TrustedSequencerURL = "https://rpc.merlinchain.io" # for testnet: https://testnet-rpc.merlinchain.io
L1SynchronizationMode = "parallel"
UpgradeEtrogBatchNumber = 1642245
[Synchronizer.L1ParallelSynchronization]
MaxClients = 10
MaxPendingNoProcessedBlocks = 25
RequestLastBlockPeriod = "5s"
RequestLastBlockTimeout = "5s"
RequestLastBlockMaxRetries = 3
StatisticsPeriod = "5m"
TimeoutMainLoop = "5m"
RollupInfoRetriesSpacing = "5s"
FallbackToSequentialModeOnSynchronized = false
[Synchronizer.L1ParallelSynchronization.PerformanceWarning]
AceptableInacctivityTime = "5s"
ApplyAfterNumRollupReceived = 10
[Etherman]
URL = "https://mainnet-l1.merlinchain.io" # for testnet: https://testnet-l1.merlinchain.io
ForkIDChunkSize = 20000
MultiGasProvider = false
[RPC]
Host = "0.0.0.0"
Port = 8123
ReadTimeout = "360s"
WriteTimeout = "360s"
MaxRequestsPerIPAndSecond = 10000
SequencerNodeURI = "https://rpc.merlinchain.io" # for testnet: https://testnet-rpc.merlinchain.io
EnableL2SuggestedGasPricePolling = false
TraceBatchUseHTTPS = true
BatchRequestsEnabled = true
BatchRequestsLimit = 10000
[RPC.WebSockets]
Enabled = true
Host = "0.0.0.0"
Port = 8133
[State]
[State.DB]
User = "state_user"
Password = "state_password"
Name = "state_db"
Host = "merlin-state-db"
Port = "5432"
EnableLog = false
MaxConns = 800
[State.Batch]
[State.Batch.Constraints]
MaxTxsPerBatch = 300
MaxBatchBytesSize = 120000
MaxCumulativeGasUsed = 1125899906842624
MaxKeccakHashes = 2145
MaxPoseidonHashes = 252357
MaxPoseidonPaddings = 135191
MaxMemAligns = 236585
MaxArithmetics = 236585
MaxBinaries = 473170
MaxSteps = 7570538
MaxSHA256Hashes = 1596
[Pool]
FreeClaimGasLimit = 1500000
IntervalToRefreshBlockedAddresses = "5m"
IntervalToRefreshGasPrices = "5s"
MaxTxBytesSize = 100132
MaxTxDataBytesSize = 100000
DefaultMinGasPriceAllowed = 6000000
MinAllowedGasPriceInterval = "5m"
PollMinAllowedGasPriceInterval = "15s"
AccountQueue = 100
GlobalQueue = 10240
[Pool.EffectiveGasPrice]
Enabled = false
L1GasPriceFactor = 0.25
ByteGasCost = 16
ZeroByteGasCost = 4
NetProfit = 1
BreakEvenFactor = 1.1
FinalDeviationPct = 10
EthTransferGasPrice = 0
EthTransferL1GasPriceFactor = 0
L2GasPriceSuggesterFactor = 0.5
[Pool.DB]
User = "pool_user"
Password = "pool_password"
Name = "pool_db"
Host = "merlin-pool-db"
Port = "5432"
EnableLog = false
MaxConns = 800
[Metrics]
Host = "0.0.0.0"
Port = 9091
Enabled = true
ProfilingHost = "0.0.0.0"
ProfilingPort = 6060
ProfilingEnabled = true
[MTClient]
URI = "cdk-validium-prover:50061"
[Executor]
URI = "cdk-validium-prover:50071"
MaxResourceExhaustedAttempts = 5
WaitOnResourceExhaustion = "1s"
MaxGRPCMessageSize = 300000000
prover.config.json
Update databaseURL if you use a customized database configuration.
{
"configPath": "/usr/src/app/config",
"runExecutorServer": true,
"runExecutorClient": false,
"runExecutorClientMultithread": false,
"runHashDBServer": true,
"runHashDBTest": false,
"runAggregatorServer": false,
"runAggregatorClient": false,
"runFileGenBatchProof": false,
"runFileGenAggregatedProof": false,
"runFileGenFinalProof": false,
"runFileProcessBatch": false,
"runFileProcessBatchMultithread": false,
"runKeccakScriptGenerator": false,
"runKeccakTest": false,
"runStorageSMTest": false,
"runBinarySMTest": false,
"runMemAlignSMTest": false,
"runSHA256Test": false,
"runBlakeTest": false,
"executeInParallel": true,
"useMainExecGenerated": true,
"saveRequestToFile": false,
"saveInputToFile": false,
"saveDbReadsToFile": false,
"saveDbReadsToFileOnChange": false,
"saveOutputToFile": true,
"saveProofToFile": true,
"saveResponseToFile": false,
"loadDBToMemCache": true,
"opcodeTracer": false,
"logRemoteDbReads": false,
"logExecutorServerResponses": false,
"proverServerPort": 50051,
"proverServerMockPort": 50052,
"proverServerMockTimeout": 10000000,
"proverClientPort": 50051,
"proverClientHost": "127.0.0.1",
"executorServerPort": 50071,
"executorROMLineTraces": false,
"executorClientPort": 50071,
"executorClientHost": "127.0.0.1",
"hashDBServerPort": 50061,
"hashDBURL": "local",
"aggregatorServerPort": 50081,
"aggregatorClientPort": 50081,
"aggregatorClientHost": "zkevm-node",
"mapConstPolsFile": false,
"mapConstantsTreeFile": false,
"inputFile": "input_executor_0.json",
"inputFile2": "input_executor_1.json",
"keccakScriptFile": "config/scripts/keccak_script.json",
"storageRomFile": "config/scripts/storage_sm_rom.json",
"outputPath": "output",
"databaseURL": "postgresql://prover_user:prover_password@merlin-state-db:5432/prover_db",
"dbNodesTableName": "state.nodes",
"dbProgramTableName": "state.program",
"dbMultiWrite": true,
"dbFlushInParallel": false,
"dbMTCacheSize": 1024,
"dbProgramCacheSize": 512,
"dbNumberOfPoolConnections": 2000,
"dbGetTree": true,
"cleanerPollingPeriod": 600,
"requestsPersistence": 3600,
"maxExecutorThreads": 500,
"maxProverThreads": 20,
"maxHashDBThreads": 300,
"ECRecoverPrecalc": false,
"ECRecoverPrecalcNThreads": 4,
"stateManager": true,
"useAssociativeCache": false
}
genesis.json
testnet
https://download.merlinchain.io/merlin/testnet/genesis.json
mainnet
https://download.merlinchain.io/merlin/mainnet/genesis.json
Launch database
Database Server
docker compose up -d cdk-validium-state-db
Snapshot Recovery
Install postgresql-client-15
# Install dependency
sudo apt install gnupg2 wget vim -y
# Enable official package repo
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
# Import GPG signing key
wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /etc/apt/trusted.gpg.d/pgdg.asc &>/dev/null
# Update system packages
sudo apt update -y
# Install postgres client
sudo apt install postgresql-client-15 -y
Download recovery database snapshot
# Confirm the creation date of below downloaded files; they must be on the same day.
# Testnet
wget https://rpc-snapshot.merlinchain.io/testnet_state_db.sql.tar.gz
wget https://rpc-snapshot.merlinchain.io/testnet_prover_db.sql.tar.gz
# Mainnet
wget https://rpc-snapshot.merlinchain.io/state_db.sql.tar.gz
wget https://rpc-snapshot.merlinchain.io/prover_db.sql.tar.gz
Prepare recovery script
git clone https://github.com/0xPolygon/cdk-validium-node.git
cd cdk-validium-node
# Recommend using Go 1.21 or a later version
go build -o ./build ./cmd
Prepare recovery configuration
# touch a config file named "snapshot_restore.toml"
vim snapshot_restore.toml
# update the database connection info based on your "docker-compose.yml"
[Log]
Environment = "production" # "production" or "development"
Level = "debug" # "info"
Outputs = ["stderr"]
[State]
[State.DB]
User = "state_user"
Password = "state_password"
Name = "state_db"
Host = "127.0.0.1"
Port = "5432"
EnableLog = false
MaxConns = 200
[HashDB]
User = "prover_user"
Password = "prover_password"
Name = "prover_db"
Host = "127.0.0.1"
Port = "5432"
EnableLog = false
MaxConns = 200
Execute recovery
# Assuming you are currently under previous Git directory, and the snapshot recovery file you downloaded is also in the current directory (or you can manually specify the location), execute the following command for recovery:
./build restore --cfg ./snapshot_restore.toml -is ./state_db.sql.tar.gz -ih ./prover_db.sql.tar.gz
Add extra index for state db
CREATE INDEX IF NOT EXISTS l2block_block_hash_idx ON state.l2block (block_hash);
CREATE INDEX IF NOT EXISTS l2block_created_at_idx ON state.l2block (created_at);
CREATE INDEX IF NOT EXISTS log_log_index_idx ON state.log (log_index);
CREATE INDEX IF NOT EXISTS log_topic0_idx ON state.log (topic0);
CREATE INDEX IF NOT EXISTS log_topic1_idx ON state.log (topic1);
CREATE INDEX IF NOT EXISTS log_topic2_idx ON state.log (topic2);
CREATE INDEX IF NOT EXISTS log_topic3_idx ON state.log (topic3);
CREATE INDEX IF NOT EXISTS idx_receipt_tx_index ON state.receipt (block_num, tx_index);
CREATE INDEX IF NOT EXISTS receipt_block_num_idx ON state.receipt USING btree (block_num);
Launch other containers
Sync Server
docker compose up -d cdk-validium-prover
docker compose up -d cdk-validium-pool-db
sleep 5
docker compose up -d cdk-validium-sync
RPC Server
docker compose up -d cdk-validium-prover
docker compose up -d cdk-validium-pool-db
sleep 5
docker compose up -d cdk-validium-json-rpc
Installation Verification
On the RPC host, execute the following command:
curl --location 'http://localhost:8123' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
The expected result should be a valid block number:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x2855"
}
Troubleshooting
Please note that there should be no indication of port occupation when checking the port mappings for Docker.
Ensure that in all your configuration files, the databases have the correct host / user / password value. You can test connectivity using the command line before proceeding with the installation.
Commercial RPC nodes
For reference, you can also subscribe to the following third-party RPC vendors.
BlockPI: https://blockpi.io/
.......
Last updated