Run Mainnet node

Unit Zero is an EVM-compatible blockchain network built on top of the Waves blockchain. This section explains how to operate a node on Unit Zero Mainnet.

You have two options:

  • Run a validating node without mining blocks

  • Run a mining node and get rewards

In both options, your node synchronizes with the blockchain network and can propose new transactions. Running a validating node has no requirements for token balances.

Prerequisites

  • Meet hardware requirements: 4 CPU cores, 8 GB RAM, 250+ GB SSD.

  • Get the up-to-date blockchain data of Waves Mainnet. The quickest method it is to download the blockchain_last.tar archive from http://blockchain.wavesnodes.com/ and unpack it. Other methods are detailed in the Synchronize Waves Blockchain section of the Waves documentation.

  • Install Docker if it is not already installed.

  • Create a Waves Mainnet account using one of the Waves-enabled wallets.

Additionally, if you'd like your node to mine:

  • Transfer some WAVES to the node’s account to cover fees.

    Note: The Consensus Client will invoke the Chain Contract on behalf of the account. Therefore, if you set a verifier for the account, the verifier must allow such transactions.

  • Lease (delegate) at least 20,000 WAVES to the node’s account to achieve the minimum generating balance.

Configuration

  1. Create a directory to serve as the main directory for storing data and configurations. Ensure the current user has read and write access to this directory.

  2. Create the data/waves subdirectory and place the Waves Mainnet blockchain data inside it.

  3. Create the following subdirectories in the main directory:

    mkdir -p data/besu logs/besu
  4. Grant the user with uid 1000 write access to the data/besu and logs/besu subdirectories:

    chown 1000 data/besu logs/besu
  5. Generate node key and JWT secret:

    mkdir -p data/secrets
    openssl rand -hex 32 | tr -d "\n" > data/secrets/p2p-key
    openssl rand -hex 32 | tr -d "\n" > data/secrets/jwtsecret
  6. Prepare the waves.conf file using the template provided below:

    • Insert your Waves Mainnet account's seed converted to a byte array and then encoded in base58, and choose an arbitrary string as a password.

    • Specify your server's static IP address. If your server does not have a static IP address, do not add the declared-address line.

    Place this file in the main directory.

    waves {
      blockchain.type = MAINNET
      extensions = [
        units.ConsensusClient
      ]
    
      wallet {
        seed = <your-base58-encoded-seed>
        password = <some-string-as-password>
        file = null
      }
    
      network {
        port = 6868
        declared-address = "<your server's IP address>:6868"   # Example: 11.22.33.44:6868.
      }
    
      rest-api.bind-address = "0.0.0.0"
    }
    
    units {
      defaults {
        jwt-secret-file = /etc/secrets/jwtsecret
      }
      chains = [{
        execution-client-address = "http://besu:8551"
        chain-contract = 3PKgN8rfmvF7hK7RWJbpvkh59e1pQkUzero
        network {
          port = 6865
          known-peers = [
            "mainnet-htz-fsn1-1.unit0.dev:6865"
            "mainnet-htz-hel1-12.wavesnodes.com:6865"
          ]
          declared-address = "<your server's IP address>:6865"   # Example: 11.22.33.44:6865
        }
        mining-enable = yes # set to "no" to run a validating node
      }]
    }
  7. Place the following genesis.json file into the main directory.

    {
      "alloc": {
        "0x0000000000000000000000000000000000006a7e": {
          "code": "0x60806040526004361061006e575f3560e01c806396f396c31161004c57806396f396c3146100e3578063c4a4326d14610105578063e984df0e1461011d578063fccc281314610131575f80fd5b806339dd5d1b146100725780637157405a146100b957806378338413146100ce575b5f80fd5b34801561007d575f80fd5b506100a161008c36600461059e565b5f6020819052908152604090205461ffff1681565b60405161ffff90911681526020015b60405180910390f35b3480156100c4575f80fd5b506100a161040081565b6100e16100dc3660046105b5565b61015c565b005b3480156100ee575f80fd5b506100f761044e565b6040519081526020016100b0565b348015610110575f80fd5b506100f76402540be40081565b348015610128575f80fd5b506100f7610468565b34801561013c575f80fd5b506101445f81565b6040516001600160a01b0390911681526020016100b0565b61016c6402540be40060016105fc565b34101561017834610478565b61019061018b6402540be40060016105fc565b610478565b6040516020016101a1929190610630565b604051602081830303815290604052906101d75760405162461bcd60e51b81526004016101ce9190610688565b60405180910390fd5b506101ef6402540be400677fffffffffffffff6105fc565b3411156101fb34610478565b61021561018b6402540be400677fffffffffffffff6105fc565b6040516020016102269291906106bd565b604051602081830303815290604052906102535760405162461bcd60e51b81526004016101ce9190610688565b50435f8181526020819052604090205461ffff166104009081119061027790610478565b604051602001610287919061070c565b604051602081830303815290604052906102b45760405162461bcd60e51b81526004016101ce9190610688565b505f818152602081905260408120805461ffff16916102d283610786565b91906101000a81548161ffff021916908361ffff160217905550505f6402540be400346102ff91906107a6565b9050346103116402540be400836105fc565b1461031b34610478565b6103296402540be400610478565b60405160200161033a9291906107c5565b604051602081830303815290604052906103675760405162461bcd60e51b81526004016101ce9190610688565b506040515f90819034908281818185825af1925050503d805f81146103a7576040519150601f19603f3d011682016040523d82523d5f602084013e6103ac565b606091505b50509050806103fd5760405162461bcd60e51b815260206004820152601e60248201527f4661696c656420746f2073656e6420746f206275726e2061646472657373000060448201526064016101ce565b604080516bffffffffffffffffffffffff1986168152600784900b60208201527ffeadaf04de8d7c2594453835b9a93b747e20e7a09a7fdb9280579a6dbaf131a8910160405180910390a150505050565b6104656402540be400677fffffffffffffff6105fc565b81565b6104656402540be40060016105fc565b6060815f0361049e5750506040805180820190915260018152600360fc1b602082015290565b815f5b81156104c757806104b181610814565b91506104c09050600a836107a6565b91506104a1565b5f8167ffffffffffffffff8111156104e1576104e161082c565b6040519080825280601f01601f19166020018201604052801561050b576020820181803683370190505b509050815b851561059557610521600182610840565b90505f61052f600a886107a6565b61053a90600a6105fc565b6105449088610840565b61054f906030610853565b90505f8160f81b90508084848151811061056b5761056b61086c565b60200101906001600160f81b03191690815f1a90535061058c600a896107a6565b97505050610510565b50949350505050565b5f602082840312156105ae575f80fd5b5035919050565b5f602082840312156105c5575f80fd5b81356bffffffffffffffffffffffff19811681146105e1575f80fd5b9392505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610613576106136105e8565b92915050565b5f81518060208401855e5f93019283525090919050565b6a029b2b73a103b30b63ab2960ad1b81525f61064f600b830185610619565b7f206d7573742062652067726561746572206f7220657175616c20746f20000000815261067f601d820185610619565b95945050505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6a029b2b73a103b30b63ab2960ad1b81525f6106dc600b830185610619565b7f206d757374206265206c657373206f7220657175616c20746f20000000000000815261067f601a820185610619565b7f4d6178207472616e7366657273206c696d6974206f662000000000000000000081525f61073d6017830184610619565b7f207265616368656420696e207468697320626c6f636b2e2054727920746f207381527232b732103a3930b739b332b9399030b3b0b4b760691b60208201526033019392505050565b5f61ffff821661ffff810361079d5761079d6105e8565b60010192915050565b5f826107c057634e487b7160e01b5f52601260045260245ffd5b500490565b6a029b2b73a103b30b63ab2960ad1b81525f6107e4600b830185610619565b7f206d7573742062652061206d756c7469706c65206f6620000000000000000000815261067f6017820185610619565b5f60018201610825576108256105e8565b5060010190565b634e487b7160e01b5f52604160045260245ffd5b81810381811115610613576106136105e8565b60ff8181168382160190811115610613576106136105e8565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220106399f534da089226c14e2f183f8421d059a924c65c97d7e4f3e931c54fe1bb64736f6c634300081a0033",
          "balance": "0x0"
        }
      },
      "baseFeePerGas": "0x3b9aca00",
      "blobGasUsed": null,
      "coinbase": "0x0000000000000000000000000000000000000000",
      "config": {
        "chainId": 88811,
        "arrowGlacierBlock": 0,
        "berlinBlock": 0,
        "byzantiumBlock": 0,
        "cancunTime": 0,
        "constantinopleBlock": 0,
        "daoForkBlock": 0,
        "eip150Block": 0,
        "eip155Block": 0,
        "eip158Block": 0,
        "ethash": {},
        "grayGlacierBlock": 0,
        "homesteadBlock": 0,
        "istanbulBlock": 0,
        "londonBlock": 0,
        "muirGlacierBlock": 0,
        "petersburgBlock": 0,
        "shanghaiTime": 0,
        "terminalTotalDifficulty": 0,
        "terminalTotalDifficultyPassed": true
      },
      "difficulty": "0x0",
      "excessBlobGas": null,
      "extraData": "0x",
      "gasLimit": "0x989680",
      "gasUsed": "0x0",
      "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "nonce": "0x0",
      "number": "0x0",
      "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
      "timestamp": "0x6720960b"
    }
  8. Place the following log4j2.xml file into the main directory.

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="INFO" monitorInterval="5">
    
        <Properties>
            <Property name="root.log.pattern">%date %-5level [%-25.25thread] %35.35c{1.} - %msg%n%throwable</Property>
        </Properties>
    
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout alwaysWriteExceptions="false" pattern='${root.log.pattern}'/>
            </Console>
            <RollingFile name="RollingFile" fileName="/opt/besu/logs/besu.log" filePattern="/opt/besu/logs/besu-%d{yyyy-MM-dd}-%i.log.gz" >
                <PatternLayout alwaysWriteExceptions="false" pattern='${root.log.pattern}'/>
                <Policies>
                    <TimeBasedTriggeringPolicy />
                    <SizeBasedTriggeringPolicy size="1000 MB" />
                </Policies>
                <DefaultRolloverStrategy max="20"/>
            </RollingFile>
        </Appenders>
    
        <Loggers>
            <Logger name="oshi" level="OFF" additivity="false"/>
            <Logger name="io.vertx" level="OFF" additivity="false"/>
            <Root>
                <AppenderRef ref="Console" level="INFO" />
                <AppenderRef ref="RollingFile" level="TRACE" />
            </Root>
        </Loggers>
    
    </Configuration>
  9. Place the following static-nodes.json file into the main directory.

    [
      "enode://02718d9a9c6f829c14ea3fd143e526130c70ae43956682cd234046ec932b1c21ca321c484bbb6aa766322ee7e0089d3405747f31d156ec0d67cd80482aede5d7@81.82.238.146:30301",
      "enode://02884ba9163fe92e1aa5a940a050d12c7d857fed092e43d242ebb63472a3e16e77d3b626d50ad790ed454e0e93607ba7ccebd5ac8423d5745df5066708709ac9@185.241.151.12:30303",
      "enode://1937134aff8287fddfc80d2583c9218521b15719dc7782ec85322493b973d02a76edf23ad30657f2d827e04a573e8a5bb2a0052a696a960365c2f13e69cbc0f3@95.216.152.163:30303",
      "enode://5355b769f711ab1951c788d664d0ccbf4bc989013ea3a4f419ab01d9930b524956be65442fa4b72e7de9c470ceeac796235fb198ad9ef49dab559139f17282e2@38.242.222.27:30303",
      "enode://6b534a07943cbca55d6dff834cac2c77f3e82895f51b25fa8cca1689b5cc0921f68453165fa5c736e55e4c61d0b73795445ed2099c09008358805467d07d2066@81.82.238.146:30302",
      "enode://85389460716e3e3247b51139de2f11f1b886c7eb3dc849e2e9673447c81922f9955b2d271834cae9c1625e69f7805e2c89362d4dd8cb0ca972668d7262252790@63.250.53.184:30303",
      "enode://b232ad7ca7bd6156e1adaa28ee0d064f3d973a67c0ad507a1b9dc32b3d64b307c754bc48c274aa96852f0041c2f85792681495e21f5baa62f06d3db8097af258@142.132.251.180:30303",
      "enode://ded10bb464eea90d2a61ab7fef15531dca698845d93f7fc191a5383c22b09de282982872515f398ae527d300c2d5324bae61199dbb354843e6792f2d13b87880@172.104.241.53:30303",
      "enode://e15d6f32cddc2a8e69885fc5c445b8ca7bafd91e9a7a834e89ed688150aa55df20fc7ff14d1fc5dd1e266c2b6a2f9dc75b1d6ed5f80ac71cb27f6ba8af12ba4b@213.238.172.133:30303",
      "enode://e8f7dbec4b146ef0ed1852cd05e3c0654f78a5784a7584f45bc0073bf6206d55764600b586148dfca42bd711c1c1bc02cb20abb2210320f830f003aacf074396@95.217.59.114:30303",
      "enode://edab526402907ccbbdf51950f09b964d9ebc1b51e1a9a1dca44fdaf823d85f84e513e95503f1e4d9e4e90355674819d52e46b014e054da5f199d5406ae6a5e22@185.31.163.22:30303"
    ]
  10. Set up the docker-compose.yml file as follows, assuming all data resides in the same directory. You may adjust the paths on your as necessary.

services:
  besu:
    container_name: besu
    image: hyperledger/besu:latest
    pull_policy: always
    stop_grace_period: 5m
    command:
      - --logging=ALL
      - --host-allowlist=*
      - --rpc-http-enabled
      - --rpc-http-api=ETH,NET,WEB3,TXPOOL,TRACE
      - --rpc-http-cors-origins=all
      - --rpc-ws-enabled
      - --discovery-enabled=true
      - --engine-rpc-enabled
      - --engine-jwt-secret=/etc/secrets/jwtsecret
      - --engine-host-allowlist=*
      - --node-private-key-file=/etc/secrets/p2p-key
      - --data-path=/var/lib/besu
      - --genesis-file=/etc/besu/genesis.json
      - --static-nodes-file=/etc/besu/static-nodes.json
      - --data-storage-format=BONSAI
      - --network-id=88811
      - --target-gas-limit=20000000
    volumes:
      - ./genesis.json:/etc/besu/genesis.json
      - ./data/secrets:/etc/secrets:ro
      - ./log4j2.xml:/etc/besu/log4j2.xml
      - ./data/besu:/var/lib/besu
      - ./logs/besu:/opt/besu/logs
      - ./static-nodes.json:/etc/besu/static-nodes.json
    ports:
      - '30303:30303/tcp'
      - '30303:30303/udp'
      - '127.0.0.1:8545:8545'
    environment:
      - LOG4J_CONFIGURATION_FILE=/etc/besu/log4j2.xml

  waves-node:
    container_name: waves-node
    image: ghcr.io/unitsnetwork/consensus-client:temurin-21
    stop_grace_period: 5m
    ports:
      - "6865:6865"
      - "6868:6868"
      - "127.0.0.1:6869:6869"
    environment:
      - JAVA_OPTS=-Dwaves.config.directory=/etc/waves -Dlogback.file.level=TRACE --add-opens=java.base/sun.nio.ch=ALL-UNNAMED
    volumes:
      - ./data/secrets:/etc/secrets:ro
      - ./data/waves:/var/lib/waves/data
      - ./waves.conf:/etc/waves/waves.conf:ro
      - ./logs/waves:/var/log/waves
    depends_on:
      besu-check:
        condition: service_completed_successfully

Docker and UFW interaction

There's a known incompatibility between docker and UFW. This incompatibility makes it impossible to map the container's port to host port with e.g. '8545:8545' and restrict access from external addresses with UFW. The sample docker-compose.yml maps Besu's JSON RPC port 8545 and Waves node's HTTP API port 6869 to the host's local address ('127.0.0.1:8545:8545' and '127.0.0.1:6869:6869'), making the endpoints available only to the consumers running on the host. If you intend to make JSON RPC or HTTP API reachable from external addresses, change port mapping to '8545:8545' and '6869:6869', or consider using reverse proxy like nginx.

Launch node

Start your node using the following command:

docker-compose up -d

Ensure the Consensus Client is synchronized with the network, as indicated by the message "Execution chain is synced" in the Waves log.

Start mining

Invoke the join() function of the Mainnet Chain Contract on behalf of your node’s Waves account. Specify your address on the Unit Zero Mainnet as an argument to receive epoch rewards and transaction fees.

Your node will begin mining when its turn comes.

If you encounter any difficulties, feel free to reach out via Developer chat for assistance.

Last updated