- [zkEVM](#zkevm)
- [Requisites](#requisites)
- [Variables](#variables)
- [Resources](#resources)
- [Dependences](#dependences)
- [Mainnet files](#mainnet-files)
- [Deploy contracts](#deploy-contracts)
- [Create wallets](#create-wallets)
- [Prepare configuration](#prepare-configuration)
- [Deploy contracts](#deploy-contracts-1)
- [zkNode deployment](#zknode-deployment)
- [Aprove Matic token for sequencer](#aprove-matic-token-for-sequencer)
- [Configure genesis](#configure-genesis)
- [Change config](#change-config)
- [Add wallets](#add-wallets)
- [Edit DBs](#edit-dbs)
- [Configure prover](#configure-prover)
- [Configure services](#configure-services)
- [Start services](#start-services)
- [Open Ports](#open-ports)
- [Activate forced transactions](#activate-forced-transactions)
- [Provide L1 Ether to the bridge](#provide-l1-ether-to-the-bridge)
- [Claim our L2 zkEther](#claim-our-l2-zkether)
- [\[Extra\] L1 Goerli fullnode](#extra-l1-goerli-fullnode)
- [Requirements](#requirements)
- [Resources](#resources-1)
- [Software](#software)
- [L1 account](#l1-account)
- [Preparation](#preparation)
- [Deployment](#deployment)
- [Validation](#validation)
- [\[OPTIONAL\] HTTPS Reverse Proxy](#optional-https-reverse-proxy)
# zkEVM
## Requisites
### Variables
* INFURA_PROJECT_ID.
* ETHERSCAN_API_KEY.
* IP public address.
* L1 Goerli node RPC.
* Goerli address with 15 GöETH.
### Resources
The zkEVM is highly resource consuming, for that reason, we propose two different recommended resources depending if you want to have a full prover or not.
The required resources for a mock prover are:
* 32 vcpu
* 256gb of memory
The required resources for a full prover are:
* 96 vcpu
* 768gb of memory
An example of suitable virtual machines in public cloud would be:
* AWS: https://aws.amazon.com/ec2/instance-types/r6a/
* r6a.8xlarge for mock prover.
* r6a.24xlarge for full prover.
Regarding the disk usage, the initial space required is minimal (<1gb) but as the network runs, you should monitor the avaiable
space since the system is always appending data.
----
## Dependences
First, install the following dependences:
```bash
# APT dependences
sudo apt update -y
sudo apt install -y tmux git curl unzip jq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Install docker
sudo usermod -aG docker $USER
newgrp docker && newgrp $USER
# Nodejs (NVM)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source ~/.bashrc
nvm install 16
node -v
# v16.20.0
# Install go
wget https://go.dev/dl/go1.20.4.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.4.linux-amd64.tar.gz
rm -rf go1.20.4.linux-amd64.tar.gz
echo '
export ZKEVM_NET=mainnet
export ZKEVM_DIR=~/zkevm/zkevm-node
export ZKEVM_CONFIG_DIR=~/zkevm/zkevm-config
[ -d "/usr/local/go/bin" ] && PATH="/usr/local/go/bin:$PATH"
' >> ~/.profile
source .profile
go version
# go version go1.20.4 linux/amd64
cd zkevm
```
## Mainnet files
It is recommended to exec the wget on a tmux or byobu because the download is over 70GB. So if there's any network problem the download could continue from previous fails and not from scra>
```bash
tmux
$ wget https://de012a78750e59b808d922b39535e862.s3.eu-west-1.amazonaws.com/v1.1.0-rc.1-fork.4.tgz
ctrl + d
...
# File is over 70GB. Once finished, extract it
tar xzvf v1.1.0-rc.1-fork.4.tgz
```
## Deploy contracts
Get the contracts from Github.
```bash
git clone https://github.com/0xPolygonHermez/zkevm-contracts.git
cd zkevm-contracts
npm i
```
### Create wallets
Create wallets.js file:
```js
const ethers = require("ethers");
async function main() {
const arrayNames = ["## Deployment Address", "\n\n## Trusted sequencer", "\n\n## Trusted aggregator"];
for (let i = 0; i < arrayNames.length; i++) {
const wallet = ethers.Wallet.createRandom();
console.log(arrayNames[i]);
console.log(`Address: ${wallet.address}`);
console.log(`PrvKey: ${wallet._signingKey().privateKey}`);
console.log(`mnemonic: "${wallet._mnemonic().phrase}"`);
const keystoreJson = await wallet.encrypt("password");
console.log(`keystore: ${keystoreJson}`);
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
```
Generate wallerts
```bash
node wallets.js | tee wallets.txt
```
### Prepare configuration
Edit environment variables:
```bash
cp .env.example .env
nano .env
[
MNEMONIC="..." #(from wallets.txt Deployment Address mnemonic)
INFURA_PROJECT_ID="..." #(your infura project ID)
ETHERSCAN_API_KEY="..." #(your etherscan API key)
]
```
Edit deploy_parameters.json
```bash
cd deployment
cp deploy_parameters.json.example deploy_parameters.json
nano deploy_parameters.json
```
```json
{
"realVerifier": true,
"trustedSequencerURL": "http://X.X.X.X:8545", // your public IP
"networkName": "zkevm",
"version": "0.0.1",
"trustedSequencer": "", // from wallets.txt Trusted sequencer
"chainID": 42069, // put your prefered id
"trustedAggregator": "", // from wallets.txt aggregator sequencer
"trustedAggregatorTimeout": 604799,
"pendingStateTimeout": 604799,
"forkID": 4,
"admin": "", // from wallets.txt Deployment Address
"zkEVMOwner": "", // from wallets.txt Deployment Address
"timelockAddress": "", // from wallets.txt Deployment Address
"minDelayTimelock": 1,
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"initialZkEVMDeployerOwner": "", // from wallets.txt Deployment Address
"maticTokenAddress": "", // put an existing contract address or leave it empty auto-deploy a new contract
"zkEVMDeployerAddress": "", // put an existing contract address or leave it empty auto-deploy a new contract
"deployerPvtKey": "",
"maxFeePerGas": "",
"maxPriorityFeePerGas": "",
"multiplierGas": ""
}
```
At this point, you need to send the (15 GöETH) to Deployment address from wallets.txt.
Then, you have to adjust the gasPrice accouding to the network status. In case of Goerli, you could check it with the following command adding your ETHERSCAN_API_KEY:
```bash
# Put your Etherscan api key
ETHERSCAN_API_KEY="" echo "$(($(printf "%d\n" $(curl -s "https://api-goerli.etherscan.io/api?module=proxy&action=eth_gasPrice&apikey=$ETHERSCAN_API_KEY" | jq -r .result))/1000000000)) Gwei"
```
Edit ~/zkevm/zkevm-contracts/deployment/helpers/deployment-helpers.js to adjust the gasPrice according to network status. It is recommended to add 50 Gwei to the current gasPrice to be sure tran>
```js
const gasPriceKeylessDeployment = '100';
```
### Deploy contracts
```bash
npm run deploy:deployer:ZkEVM:goerli
npm run verify:deployer:ZkEVM:goerli
npm run deploy:testnet:ZkEVM:goerli
npm run verify:ZkEVM:goerli
```
The previous scripts will auto-deploy the matic token contract and the zkEVMDeployer contract if required.
You will see in the logs the verification of each smart contract deployed, but you can check it on etherscan.
* https://goerli.etherscan.io/address/0x -> Put the Deployer address from wallets.txt
## zkNode deployment
```bash
$ mkdir -p ~/zkevm/data/{statedb,pooldb} ~/zkevm/zkevm-config ~/zkevm/zkevm-node
curl -L https://github.com/0xPolygonHermez/zkevm-node/releases/latest/download/$ZKEVM_NET.zip > $ZKEVM_NET.zip && unzip -o $ZKEVM_NET.zip -d $ZKEVM_DIR && rm $ZKEVM_NET.zip
cp $ZKEVM_DIR/$ZKEVM_NET/example.env $ZKEVM_CONFIG_DIR/.env
nano $ZKEVM_CONFIG_DIR/.env
[
# URL of a JSON RPC for Ethereum mainnet
ZKEVM_NODE_ETHERMAN_URL = "http://localhost:8845" # (set a valid goerli RPC node)
# PATH WHERE THE STATEDB POSTGRES CONTAINER WILL STORE PERSISTENT DATA
ZKEVM_NODE_STATEDB_DATA_DIR = "~/zkevm/data/statedb"
# PATH WHERE THE POOLDB POSTGRES CONTAINER WILL STORE PERSISTENT DATA
ZKEVM_NODE_POOLDB_DATA_DIR = "~/zkevm/data/pooldb"
# OPTIONAL, UNCOMENT IF YOU WANT TO DO ADVANCED CONFIG
# ZKEVM_ADVANCED_CONFIG_DIR = "/should/be/same/path/as/ZKEVM_CONFIG_DIR"
]
```
### Aprove Matic token for sequencer
```bash
npx hardhat console --network goerli
```
```js
const provider = ethers.getDefaultProvider("") // set goerli RPC node
const privateKey = '' // From wallet.txt Trusted sequencer
const wallet = new ethers.Wallet(privateKey, provider);
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock', provider);
maticTokenContract = maticTokenFactory.attach("") // From ~/zkevm/zkevm-contract/deployments/goerly_***/deploy_output.json maticTokenAddress
maticTokenContractWallet = maticTokenContract.connect(wallet)
await maticTokenContractWallet.approve("", ethers.utils.parseEther("100.0")) // From ~/zkevm/zkevm-contract/deployments/goerly_***/deploy_output.json polygonZkEVMAddress
```
### Configure genesis
```bash
cp ~/zkevm/zkevm-contracts/deplyoments/goerli_***/genesis.json ~/zkevm/zkevm-node/mainnet/config/environments/public/public.genesis.config.json
nano ~/zkevm/zkevm-node/mainnet/config/environments/public/public.genesis.config.json
```
Edit file changing the following parameters from ~/zkevm/zkevm-contracts/deployments/goerli_***/deploy_output.json:
```js
{
"l1Config" : {
"chainId": 5,
"polygonZkEVMAddress": "",
"maticTokenAddress": "",
"polygonZkEVMGlobalExitRootAddress": "" // deploymentBlockNumber from ~/zkevm/zkevm-contracts/deployments/goerli_***/deploy_output.json
},
"genesisBlockNumber": 9050589,
"root": "",
"genesis":
}
```
Just take into account that polygonZkEVMGlobalExitRootAddress is called deploymentBlockNumber in deploy_output.json.
### Change config
Edit ~/zkevm/zkevm-node/mainnet/config/environments/public/public.node.config.toml
```yml
IsTrustedSequencer = true
[Log]
Environment = "development"
Level = "info"
Outputs = ["stderr"]
[StateDB]
User = "state_user"
Password = "state_password"
Name = "state_db"
Host = "zkevm-state-db"
Port = "5432"
EnableLog = false
MaxConns = 200
[Pool]
FreeClaimGasLimit = 1500000
MaxTxBytesSize=30132
MaxTxDataBytesSize=30000
DefaultMinGasPriceAllowed = 1000000000
MinAllowedGasPriceInterval = "5m"
PollMinAllowedGasPriceInterval = "15s"
[Pool.DB]
User = "pool_user"
Password = "pool_password"
Name = "pool_db"
Host = "zkevm-pool-db"
Port = "5432"
EnableLog = false
MaxConns = 200
[Etherman]
URL = "" # put a valid Goerli node
MultiGasProvider = false
L1URL = "" # put a valid Goerli node
L2URLs = ["http://zkevm-rpc:8545"]
[Etherman.Etherscan]
ApiKey = "" # Etherscan api key
[RPC]
Host = "0.0.0.0"
Port = 8545
ReadTimeoutInSec = 60
WriteTimeoutInSec = 60
MaxRequestsPerIPAndSecond = 5000
SequencerNodeURI = ""
BroadcastURI = "http://3.144.195.147:61090"
DefaultSenderAddress = "0x1111111111111111111111111111111111111111"
EnableL2SuggestedGasPricePolling = true
[RPC.WebSockets]
Enabled = true
Port = 8546
[Synchronizer]
SyncInterval = "2s"
SyncChunkSize = 100
trustedSequencerURL = ""
[MTClient]
URI = "zkevm-executor:50061"
[Executor]
URI = "zkevm-executor:50071"
[Metrics]
Host = "0.0.0.0"
Port = 9091
Enabled = true
ProfilingHost = "0.0.0.0"
ProfilingPort = 6060
ProfilingEnabled = false
[Sequencer]
WaitPeriodPoolIsEmpty = "1s"
WaitPeriodSendSequence = "15s"
LastBatchVirtualizationTimeMaxWaitPeriod = "10s"
BlocksAmountForTxsToBeDeleted = 100
FrequencyToCheckTxsForDelete = "12h"
MaxTxsPerBatch = 150
MaxBatchBytesSize = 129848
MaxCumulativeGasUsed = 30000000
MaxKeccakHashes = 468
MaxPoseidonHashes = 279620
MaxPoseidonPaddings = 149796
MaxMemAligns = 262144
MaxArithmetics = 262144
MaxBinaries = 262144
MaxSteps = 8388608
WeightBatchBytesSize = 1
WeightCumulativeGasUsed = 1
WeightKeccakHashes = 1
WeightPoseidonHashes = 1
WeightPoseidonPaddings = 1
WeightMemAligns = 1
WeightArithmetics = 1
WeightBinaries = 1
WeightSteps = 1
TxLifetimeCheckTimeout = "10m"
MaxTxLifetime = "3h"
MaxTxSizeForL1 = 131072
[Sequencer.Finalizer]
GERDeadlineTimeoutInSec = "2s"
ForcedBatchDeadlineTimeoutInSec = "60s"
SendingToL1DeadlineTimeoutInSec = "20s"
SleepDurationInMs = "100ms"
ResourcePercentageToCloseBatch = 10
GERFinalityNumberOfBlocks = 0
ClosingSignalsManagerWaitForCheckingL1Timeout = "10s"
ClosingSignalsManagerWaitForCheckingGER = "10s"
ClosingSignalsManagerWaitForCheckingForcedBatches = "10s"
ForcedBatchesFinalityNumberOfBlocks = 0
TimestampResolution = "15s"
[Sequencer.DBManager]
PoolRetrievalInterval = "500ms"
[Sequencer.Worker]
ResourceCostMultiplier = 1000
[SequenceSender]
WaitPeriodSendSequence = "5s"
LastBatchVirtualizationTimeMaxWaitPeriod = "5s"
MaxTxSizeForL1 = 131072
SenderAddress = "0x225c96B7dB4223f0244DcfC833e0bB9f40a948E4"
PrivateKeys = [{Path = "/pk/sequencer.keystore", Password = "password"}]
[Aggregator]
Host = "0.0.0.0"
Port = 50081
ForkId = 4
RetryTime = "5s"
VerifyProofInterval = "30s"
TxProfitabilityCheckerType = "acceptall"
TxProfitabilityMinReward = "1.1"
ProofStatePollingInterval = "5s"
SenderAddress = "" # trustedAggregator from deploy_output.json
CleanupLockedProofsInterval = "2m"
GeneratingProofCleanupThreshold = "10m"
[EthTxManager]
ForcedGas = 0
PrivateKeys = [
{Path = "/pk/sequencer.keystore", Password = "password"},
{Path = "/pk/aggregator.keystore", Password = "password"}
]
[Database]
Database = "postgres"
User = "test_user"
Password = "test_password"
Name = "test_db"
Host = "zkevm-bridge-db"
Port = "5435"
MaxConns = 20
[BridgeController]
Store = "postgres"
Height = 32
[BridgeServer]
GRPCPort = "9090"
HTTPPort = "8080"
[NetworkConfig]
GenBlockNumber = 000000 # deploymentBlockNumber from deploy_output.json
PolygonZkEVMAddress = "" # polygonZkEVMAddress from deploy_output.json
PolygonBridgeAddress = "" # PolygonZkEVMBridge from genesis.json
PolygonZkEVMGlobalExitRootAddress = "" # polygonZkEVMGlobalExitRootAddress from deploy_output.json
MaticTokenAddress = "" # maticTokenAddress from deploy_output.json
L2PolygonBridgeAddresses = [""] # PolygonZkEVMBridge from genesis.json
L1ChainID = 5 # Goerli chainID
[L2GasPriceSuggester]
Type = "default"
DefaultGasPriceWei = 100000000
[ClaimTxManager]
FrequencyToMonitorTxs = "1s"
PrivateKey = {Path = "/pk/sequencer.keystore", Password = "password"}
Enabled = true
```
### Add wallets
```bash
nano zkevm-config/sequencer.keystore (from wallets.txt)
nano zkevm-config/aggregator.keystore (from wallets.txt)
```
### Edit DBs
Edit ~/zkevm/zkevm-node/mainnet/db/scripts/init_prover_db.sql to match this:
```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_pass';
GRANT CONNECT ON DATABASE prover_db TO prover_user;
ALTER USER prover_user SET SEARCH_PATH=state;
GRANT ALL PRIVILEGES ON SCHEMA state TO prover_user;
GRANT ALL PRIVILEGES ON TABLE state.nodes TO prover_user;
GRANT ALL PRIVILEGES ON TABLE state.program TO prover_user;
```
### Configure prover
Create ~/zkevm/config.json file and put your PUBLIC IP on the paramter *aggregatorClientHost*:
```json
{
"runExecutorServer": false,
"runExecutorClient": false,
"runExecutorClientMultithread": false,
"runStateDBServer": false,
"runStateDBTest": false,
"runAggregatorServer": false,
"runAggregatorClient": true,
"proverName": "static_prover",
"runFileGenBatchProof": false,
"runFileGenAggregatedProof": false,
"runFileGenFinalProof": false,
"runFileProcessBatch": false,
"runFileProcessBatchMultithread": false,
"runFileExecutor": false,
"runKeccakScriptGenerator": false,
"runKeccakTest": false,
"runStorageSMTest": false,
"runBinarySMTest": false,
"runMemAlignSMTest": false,
"runSHA256Test": false,
"runBlakeTest": false,
"executeInParallel": true,
"useMainExecGenerated": true,
"useProcessBatchCache": true,
"saveRequestToFile": false,
"saveInputToFile": true,
"saveDbReadsToFile": true,
"saveDbReadsToFileOnChange": false,
"saveOutputToFile": true,
"saveFilesInSubfolders": false,
"saveProofToFile": true,
"saveResponseToFile": false,
"loadDBToMemCache": true,
"loadDBToMemCacheInParallel": false,
"dbMTCacheSize": 16384,
"dbProgramCacheSize": 16384,
"dbMultiWrite": true,
"dbFlushInParallel": true,
"opcodeTracer": false,
"logRemoteDbReads": false,
"logExecutorServerResponses": false,
"executorServerPort": 50071,
"executorROMLineTraces": false,
"executorClientPort": 50071,
"executorClientHost": "127.0.0.1",
"stateDBServerPort": 5432,
"stateDBURL": "local",
"aggregatorServerPort": 50081,
"aggregatorClientPort": 50081,
"aggregatorClientHost": "", // YOUR-PUBLIC-IP-ADDRESS
"aggregatorClientMockTimeout": 10000000,
"mapConstPolsFile": false,
"mapConstantsTreeFile": false,
"inputFile": "testvectors/aggregatedProof/recursive1.zkin.proof_0.json",
"inputFile2": "testvectors/aggregatedProof/recursive1.zkin.proof_1.json",
"outputPath": "/output/",
"configPath": "/mnt/prover/config/",
"zkevmCmPols_
": "/mnt/prover/runtime/zkevm.commit",
"c12aCmPols": "runtime/c12a.commit",
"recursive1CmPols_disabled": "runtime/recursive1.commit",
"recursive2CmPols_disabled": "runtime/recursive2.commit",
"recursivefCmPols_disabled": "runtime/recursivef.commit",
"finalCmPols_disabled": "runtime/final.commit",
"publicsOutput": "public.json",
"proofFile": "proof.json",
"databaseURL": "postgresql://prover_user:prover_pass@zkevm-state-db:5432/prover_db",
"databaseURL_disabled": "local",
"dbNodesTableName": "state.nodes",
"dbProgramTableName": "state.program",
"dbConnectionsPool": true,
"cleanerPollingPeriod": 600,
"requestsPersistence": 3600,
"maxExecutorThreads": 20,
"maxProverThreads": 8,
"maxStateDBThreads": 8
}
```
### Configure services
Edit the ~/zkevm/zkevm-node/mainnet/docker-compose.yml file and replace it with the following content:
```yml
version: "3.5"
networks:
default:
name: zkevm
services:
zkevm-state-db:
container_name: zkevm-state-db
restart: unless-stopped
image: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
ports:
- 5432:5432
volumes:
- ./db/scripts/init_prover_db.sql:/docker-entrypoint-initdb.d/init.sql
- ${ZKEVM_NODE_STATEDB_DATA_DIR}:/var/lib/postgresql/data
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/postgresql.conf:/etc/postgresql.conf
environment:
- POSTGRES_USER=state_user
- POSTGRES_PASSWORD=state_password
- POSTGRES_DB=state_db
command:
- "postgres"
- "-N"
- "500"
- "-c"
- "config_file=/etc/postgresql.conf"
zkevm-pool-db:
container_name: zkevm-pool-db
restart: unless-stopped
image: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 1G
ports:
- 5433:5432
volumes:
- ${ZKEVM_NODE_POOLDB_DATA_DIR}:/var/lib/postgresql/data
environment:
- POSTGRES_USER=pool_user
- POSTGRES_PASSWORD=pool_password
- POSTGRES_DB=pool_db
command:
- "postgres"
- "-N"
- "500"
zkevm-executor:
container_name: zkevm-executor
restart: unless-stopped
image: hermeznetwork/zkevm-prover:v1.1.4-RC2-fork.4
depends_on:
zkevm-state-db:
condition: service_healthy
ports:
- 50061:50061 # MT
- 50071:50071 # Executor
volumes:
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.prover.config.json:/usr/src/app/config.json
command: >
zkProver -c /usr/src/app/config.json
zkevm-sync:
container_name: zkevm-sync
restart: unless-stopped
depends_on:
zkevm-state-db:
condition: service_healthy
zkevm-executor:
condition: service_started
image: hermeznetwork/zkevm-node:v0.1.2-RC1
environment:
- ZKEVM_NODE_ETHERMAN_URL=${ZKEVM_NODE_ETHERMAN_URL}
volumes:
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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"
zkevm-l2gaspricer:
container_name: zkevm-l2gaspricer
image: hermeznetwork/zkevm-node:v0.1.2-RC1
depends_on:
zkevm-pool-db:
condition: service_healthy
environment:
- ZKEVM_NODE_POOL_DB_HOST=zkevm-pool-db
volumes:
- ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/keystore
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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 l2gaspricer"
zkevm-eth-tx-manager:
container_name: zkevm-eth-tx-manager
image: hermeznetwork/zkevm-node:v0.1.2-RC1
depends_on:
zkevm-state-db:
condition: service_healthy
ports:
- 9094:9091 # needed if metrics enabled
environment:
- ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
volumes:
- ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
- ${ZKEVM_CONFIG_DIR}/aggregator.keystore:/pk/aggregator.keystore
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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 eth-tx-manager"
zkevm-rpc:
container_name: zkevm-rpc
restart: unless-stopped
depends_on:
zkevm-pool-db:
condition: service_healthy
zkevm-state-db:
condition: service_healthy
zkevm-sync:
condition: service_started
image: hermeznetwork/zkevm-node:v0.1.2-RC1
ports:
- 8545:8545
- 9091:9091 # needed if metrics enabled
environment:
- ZKEVM_NODE_ETHERMAN_URL=${ZKEVM_NODE_ETHERMAN_URL}
volumes:
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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"
zkevm-sequencer:
container_name: zkevm-sequencer
image: hermeznetwork/zkevm-node:v0.1.2-RC1
depends_on:
zkevm-pool-db:
condition: service_healthy
zkevm-state-db:
condition: service_healthy
zkevm-executor:
condition: service_started
ports:
- 9092:9091 # needed if metrics enabled
- 6060:6060
environment:
- ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
- ZKEVM_NODE_POOL_DB_HOST=zkevm-pool-db
- ZKEVM_NODE_SEQUENCER_SENDER_ADDRESS=0x593A083125Dc0f6E50c0DAe8689ece7E22987450
volumes:
- ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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 sequencer,sequence-sender"
zkevm-aggregator:
container_name: zkevm-aggregator
image: hermeznetwork/zkevm-node:v0.1.2-RC1
depends_on:
zkevm-pool-db:
condition: service_healthy
zkevm-state-db:
condition: service_healthy
ports:
- 50081:50081
- 9093:9091 # needed if metrics enabled
environment:
- ZKEVM_NODE_STATEDB_HOST=zkevm-state-db
- ZKEVM_NODE_AGGREGATOR_SENDER_ADDRESS=0x37dB10dab4C2aEE6D161ad873D0696C4494613D0
volumes:
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.genesis.config.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 aggregator"
zkevm-explorer-l2:
container_name: zkevm-explorer-l2
image: hermeznetwork/zkevm-explorer:latest
ports:
- 4004:4004
environment:
- PORT=4004
- NETWORK=POE
- SUBNETWORK=Polygon zkEVM
- CHAIN_ID=42069
- COIN=ETH
- ETHEREUM_JSONRPC_VARIANT=geth
- ETHEREUM_JSONRPC_HTTP_URL=http://zkevm-rpc:8545
- DATABASE_URL=postgres://l2_explorer_user:l2_explorer_password@zkevm-explorer-l2-db:5432/explorer
- ECTO_USE_SSL=false
- MIX_ENV=prod
- LOGO=/images/blockscout_logo.svg
- LOGO_FOOTER=/images/blockscout_logo.svg
- SUPPORTED_CHAINS=[]
- SHOW_OUTDATED_NETWORK_MODAL=false
command: ["/bin/sh", "-c", "mix do ecto.create, ecto.migrate; mix phx.server"]
zkevm-explorer-l2-db:
container_name: zkevm-explorer-l2-db
image: postgres
ports:
- 5436:5432
environment:
- POSTGRES_USER=l2_explorer_user
- POSTGRES_PASSWORD=l2_explorer_password
- POSTGRES_DB=l2_explorer_db
command: [ "postgres", "-N", "500" ]
zkevm-bridge-db:
container_name: zkevm-bridge-db
image: postgres
healthcheck:
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
expose:
- 5435
ports:
- 5435:5432
environment:
- POSTGRES_USER=test_user
- POSTGRES_PASSWORD=test_password
- POSTGRES_DB=test_db
command: ["postgres", "-N", "500"]
zkevm-bridge-service:
container_name: zkevm-bridge-service
image: hermeznetwork/zkevm-bridge-service:v0.1.0
depends_on:
zkevm-bridge-db:
condition: service_healthy
ports:
- 9090:9090
- 8081:8080
environment:
- ZKEVM_BRIDGE_DATABASE_USER=test_user
- ZKEVM_BRIDGE_DATABASE_PASSWORD=test_password
- ZKEVM_BRIDGE_DATABASE_NAME=test_db
- ZKEVM_BRIDGE_DATABASE_HOST=zkevm-bridge-db
- ZKEVM_BRIDGE_DATABASE_PORT=5432
volumes:
- ${ZKEVM_CONFIG_DIR}/sequencer.keystore:/pk/sequencer.keystore
- ${ZKEVM_ADVANCED_CONFIG_DIR:-./config/environments/public}/public.node.config.toml:/app/config.toml
command:
- "/bin/sh"
- "-c"
- "/app/zkevm-bridge run --cfg /app/config.toml"
zkevm-bridge-ui:
container_name: zkevm-bridge-ui
image: hermeznetwork/zkevm-bridge-ui:latest
ports:
- 8080:80
environment:
- ETHEREUM_RPC_URL=$ZKEVM_NODE_ETHERMAN_URL
- ETHEREUM_EXPLORER_URL=https://goerli.etherscan.io
- ETHEREUM_BRIDGE_CONTRACT_ADDRESS=0x5567fAbF3B17F320BA3906d2f6CD43021d27A8AB
- ETHEREUM_FORCE_UPDATE_GLOBAL_EXIT_ROOT=true
- ETHEREUM_PROOF_OF_EFFICIENCY_CONTRACT_ADDRESS=0x12c3e0CC1d4619deaC19014a72C98c129448Cfd7
- POLYGON_ZK_EVM_RPC_URL=http://147.83.40.37:8545
- POLYGON_ZK_EVM_EXPLORER_URL=http://147.83.40.37:4004
- POLYGON_ZK_EVM_BRIDGE_CONTRACT_ADDRESS=0x5567fAbF3B17F320BA3906d2f6CD43021d27A8AB
- POLYGON_ZK_EVM_NETWORK_ID=1
- BRIDGE_API_URL=http://147.83.40.37:8081
- ENABLE_FIAT_EXCHANGE_RATES=false
- ENABLE_OUTDATED_NETWORK_MODAL=false
- ENABLE_DEPOSIT_WARNING=true
- ENABLE_REPORT_FORM=false
zkevm-prover-server:
container_name: zkevm-prover-server
image: hermeznetwork/zkevm-prover:v1.1.4-RC2-fork.4
ports:
- 50051:50051
volumes:
- ~/zkevm/v1.1.0-rc.1-fork.4/config:/mnt/prover/config:ro
- ~/zkevm/config.json:/app/config.json
command: "zkProver -c /app/config.json"
```
### Start services
Start databases:
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-pool-db zkevm-state-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-pool-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-state-db
```
Start executor:
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-executor
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-executor
```
Start syncronizer
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-sync
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-sync
```
Start L2 gas pricer
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-l2gaspricer
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-l2gaspricer
```
Start Transaciion manager
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-eth-tx-manager
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-eth-tx-manager
```
Start RPC
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-rpc
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-rpc
```
Start sequencer
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-sequencer
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-sequencer
```
Start aggregator
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-aggregator
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-aggregator
```
Start Explorer
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-explorer-l2 zkevm-explorer-l2-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-explorer-l2-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-explorer-l2
```
Start bridge
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-bridge-service zkevm-bridge-ui zkevm-bridge-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-db
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-service
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-bridge-ui
```
Start prover
```bash
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml up -d zkevm-prover-server
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml ps
docker compose --env-file $ZKEVM_CONFIG_DIR/.env -f $ZKEVM_DIR/$ZKEVM_NET/docker-compose.yml logs -f zkevm-prover-server
```
#### Open Ports
* zkevm-aggregator 50081->50081/tcp
* zkevm-aggregator 9093->9091/tcp
* zkevm-bridge-db 5435->5432/tcp
* zkevm-bridge-service 9090->9090/tcp, 8081->8080/tcp
* zkevm-bridge-ui 8080->80/tcp
* zkevm-eth-tx-manager 9094->9091/tcp
* zkevm-executor 50061->50061/tcp, 50071->50071/tcp
* zkevm-explorer-l2 4004->4004/tcp
* zkevm-explorer-l2-db 5436->5432/tcp
* zkevm-pool-db 5433->5432/tcp
* zkevm-prover-server 50051->50051/tcp
* zkevm-rpc 8545->8545/tcp, 9091->9091/tcp
* zkevm-sequencer 6060->6060/tcp, 9092->9091/tcp
* zkevm-state-db 5432->5432/tcp
### Activate forced transactions
```bash
npx hardhat console --network goerli
```
```js
const zkevm = await ethers.getContractFactory('PolygonZkEVM')
const zkevmContract = zkevm.attach("_polygonZkEVMAddress_")
const provider = ethers.getDefaultProvider("") // set goerli RPC node
const privateKey = '' // Deployment Address from wallet.txt
const wallet = new ethers.Wallet(privateKey, provider);
const zkevmContractWallet = zkevm.connect(wallet)
await zkevmContract.activateForceBatches()
```
### Provide L1 Ether to the bridge
We are going to create a bridge transaction to send L1 Ether to the bridge.
```bash
mkdir -p ~/zkevm/initial-bridge
cd zkevm/initial-bridge
wget https://raw.githubusercontent.com/0xPolygonHermez/zkevm-bridge-service/develop/test/scripts/deposit/main.go
nano main.go
```
```go
l1BridgeAddr = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMBridgeAddress
l1AccHexAddress = "" // zkevm-contracts/wallets.txt: sequencer address
l1AccHexPrivateKey = "" // zkevm-contracts/wallets.txt: sequencer prvkey
l1NetworkURL = "http://X.X.X.X:8545" // set your public IP
```
```bash
go mod init example.com/deposit
go mod tidy
go run main.go
# 2023-06-07T06:29:14.211Z INFO initial-bridge/main.go:46 Success! {"pid": 776268, "version": "v0.1.0"}
```
### Claim our L2 zkEther
Create a forzed claim to get the zkEther in L2.
```bash
mkdir -p ~/zkevm/initial-claim
cd zkevm/initial-claim
wget https://raw.githubusercontent.com/0xPolygonHermez/zkevm-bridge-service/develop/test/scripts/initialClaim/main.go
```
Open main.go and modify the following parameters:
```go
const (
l2BridgeAddr = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMBridgeAddress
zkevmAddr = "" // zkevm-contracts/deployments/goerli_*/deploy_output.json: polygonZkEVMAddress
accHexAddress = "" // zkevm-contracts/wallets.txt: sequencer address
accHexPrivateKey = "" // zkevm-contracts/wallets.txt: sequencer prvkey
l1NetworkURL = "http://X.X.X.X:8545" // public IP
l2NetworkURL = "http://X.X.X.X:8123" // public IP
bridgeURL = "http://X.X.X.X:8080" // public IP
l2GasLimit = 1000000
mtHeight = 32
miningTimeout = 180
)
```
```go
go mod init example.com/claim
go mod tidy
go get github.com/0xPolygonHermez/zkevm-bridge-service@4e1ca558144cac00fe0762329aaf7b3e9482b57a
go run main.go
// 2023-06-07T06:41:50.731Z INFO initial-claim/main.go:196 Success!!!! {"pid": 783804, "version": "v0.1.0"}
```
# [Extra] L1 Goerli fullnode
We are going to use the Goerli testnet as layer 1 (L1). We need to do quite a lot of transactions in L1 so it is recommended to build your own fullnode. To do so, follow the following step>
## Requirements
### Resources
A minimal of **300GB** of free disk space is required to allocate a full Goerli node.
### Software
The requirements of this tutorial are the following commands:
* wget
* jq
* docker
```bash
sudo apt update -y
sudo apt install -y docker.io
sudo usermod -aG docker $USER
newgrp docker && newgrp $USER
```
### L1 account
An L1 Goerli address is necessary to be provisioned as the *suggested fee recipient*.
## Preparation
```bash
mkdir -p ~/goerli-node/docker-volumes/{geth,prysm}
cd ~/goerli-node/
nano docker-compose.yml
```
Create a *docker-compose.yml* file
```yml
services:
geth:
image: ethereum/client-go:stable
container_name: goerli-execution
command: |
--goerli
--http
--http.vhosts=*
--http.rpcprefix=/
--http.corsdomain=*
--http.addr 0.0.0.0
--http.api eth,net,engine,admin
volumes:
- ./docker-volumes/geth:/root/.ethereum
ports:
- "0.0.0.0:${L1_RPC_PORT}:8545"
- "0.0.0.0:30303:30303/udp"
prysm:
image: gcr.io/prysmaticlabs/prysm/beacon-chain:stable
container_name: goerli-consensus
command: |
--prater
--datadir=/data
--jwt-secret=/geth/goerli/geth/jwtsecret
--rpc-host=0.0.0.0
--grpc-gateway-host=0.0.0.0
--monitoring-host=0.0.0.0
--execution-endpoint=/geth/goerli/geth.ipc
--accept-terms-of-use
--suggested-fee-recipient=${L1_SUGGESTED_FEE_RECIPIENT_ADDR}
--checkpoint-sync-url=${L1_CHECKPOINT_URL**}
volumes:
- ./docker-volumes/prysm:/data
- ./docker-volumes/geth:/geth
ports:
- "0.0.0.0:3500:3500"
- "0.0.0.0:4000:4000"
- "0.0.0.0:12000:12000/udp"
- "0.0.0.0:13000:13000"
depends_on:
- geth
```
Create an account
nano .env
```yml
L1_RPC_PORT=8845
L1_SUGGESTED_FEE_RECIPIENT_ADDR=0x # Put your Goerli account
L1_CHECKPOINT_URL=https://goerli.checkpoint-sync.ethpandaops.io
```
## Deployment
Finally, start the compose services and check the logs until everything is synchronized.
```bash
docker compose up -d
docker compose logs -f prysm
# Wait for the initial-sync goerli-consensus -> (eta 10min in our case)
#goerli-consensus | time="2023-06-19 09:39:44" level=info msg="Synced up to slot 5888296" prefix=initial-sync
docker compose logs -f geth
# Wait for the initial download & sync (a couple of hours)
#goerli-execution | INFO [06-19|09:43:24.954] Syncing beacon headers downloaded=25600 left=9,177,918 eta=1h5m31.860s
#goerli-execution | INFO [06-19|10:09:19.488] Syncing: state download in progress synced=0.30% state=331.34MiB accounts=81053@20.52MiB slots=1,112,986@239.47MiB codes=11681@71.34MiB >
```
## Validation
Once both service logs show the sync completion and there are new blocks updating the system, you could verify the correctness of the RPC doing a call. For example, you could get the current block number with the following command:
```bash
printf "%d\n" $(curl -s -X POST --header "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}' http://localhost:8845 | jq -r .result)
```
## [OPTIONAL] HTTPS Reverse Proxy
Remove exposed port in docker compose form geth and build a reverse proxy pointing this service. Some examples for reverse proxy service are nginx or traefik.
In our case, we already have a traefik on the VM. So we only need to add the public network and the correct labels to expose the service on YOUR_FQDN_NAME.
```yml
services:
geth:
...
labels:
- "traefik.enable=true"
- "traefik.docker.network=public"
- "traefik.http.routers.traefik.rule=Host(`YOUR_FQDN_NAME`)"
- "traefik.http.routers.traefik.tls.domains[0].main=YOUR_FQDN_NAME"
- "traefik.http.routers.traefik.tls.certresolver=letsEncrypt"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.services.traefik.loadbalancer.server.port=8545"
networks:
- public
- goerli
prysm:
...
networks:
- goerli
networks:
goerli:
public:
external: true
```