How to deploy a local testnet

To start with Klever blockchain, make sure you have Docker and Docker-Compose installed and download the latest Klever Toolchain image.

Follow instructions on https://docs.docker.com/engine/install to install Docker.

How many validators do you want?

The first thing we must do when creating our local blockchain is know how many validator nodes you want to use. For this tutorial the default will be 2 validators.

Generate Keys

To generate the necessary keys you will need to execute the command below. If you want to generate a different number of validator nodes, just change the value in --num-keys=2

    docker run -it --rm -v $(pwd):/opt/klever-blockchain \
        --user "$(id -u):$(id -g)" \
        --entrypoint='' kleverapp/klever-go:latest keygenerator --num-keys=2 --key-type=both

The file structure you will have after running the command will be this.

    localchain
    ├── node-0
    │   ├── validatorKey.pem
    │   └── walletKey.pem
    └── node-1
        ├── validatorKey.pem
        └── walletKey.pem

For our examples in this tutorial, we will assume that the data generated by the command is the following:

Node 0:
- Address 0: klv1lay4f5657l535g7j5nx8wenhqvlhjune2zfvt94sv5kv7qk3snwqajpux3
- BLS 0: 158449b4219fff1151d2e0eaf50c63cb95ba8b582ac2ff5429f90f500dd816068ac071c5a13490b65296b22a5fb0ab07359029a3fce0761441dd2c820713e52fc0a8ef3b9a0d8af28c9f2b74b5a5542956e481e596911b559b8c76cb66fa0685
Node 1:
- Address 1: klv14nwrysndqs29wvvskwk8lq3ch9v4jatf6l6mxa6vrj9wylt0gxssw0xvmm
- BLS 1: 9eb5fe005d5ad50a621ac9728ed2dcaf780c1d69fae86399997d0b9bc3f53c1b968a56a048f083b60290ebd037267f12a2965220100e85324b3a68fd940574873b7bed6d6cdf66fa404f337e52e306010d8297f0d89c9b04e4514c7c7536d293

Node Config

Any node needs the following configuration files to run successfully.

  • nodesSetup.json
  • genesis.json
  • api.yaml
  • config.yaml
  • enableEpochs.yaml
  • external.yaml

To make configuring the nodes easier, we will download the klever mainnet configuration files and edit them as we need. To download the configuration files, just run the command below.

    curl -k https://backup.mainnet.klever.finance/config.mainnet.108.tar.gz \
        | tar -xz -C .

The file structure you will have after running the command will be this.

    localchain
    ├── config
    │   ├── api.yaml
    │   ├── config.yaml
    │   ├── enableEpochs.yaml
    │   ├── external.yaml
    │   ├── genesis.json
    │   └── nodesSetup.json
    ├── node-0
    │   ├── validatorKey.pem
    │   └── walletKey.pem
    └── node-1
        ├── validatorKey.pem
        └── walletKey.pem

P.S.: You can use the same configurations for all nodes, but if you want to have a node indexer, I recommend you make a separate configuration just for that node.

genesis.json

In genesis.json you should have a list that will contain data from each genesis node. First, we will delete the current configuration and write it according to our generated keys.

Ex:

    {
        "address": "klv1vx2j8lwdskc9z5mr9z2ttlpfpaf6dkxes7mf7zg883x2lcl5m9dselkspy", // Validator Address
        "balance": 3323333333333333, // KLV Balance
        "kfiBalance": 7000000000000, // KFI Balance
        "delegation": { // Delegation List
            "address": "klv1vx2j8lwdskc9z5mr9z2ttlpfpaf6dkxes7mf7zg883x2lcl5m9dselkspy", // Self Delegation
            "value": 10000000000000
        }
    }

Based on the data we generate, this is what the configuration will look like:

[
  {
      "address": "klv1lay4f5657l535g7j5nx8wenhqvlhjune2zfvt94sv5kv7qk3snwqajpux3",
      "balance": 3323333333333333,
      "kfiBalance": 7000000000000,
      "delegation": {
          "address": "klv1lay4f5657l535g7j5nx8wenhqvlhjune2zfvt94sv5kv7qk3snwqajpux3",
          "value": 10000000000000
      }
  },
  {
    "address": "klv14nwrysndqs29wvvskwk8lq3ch9v4jatf6l6mxa6vrj9wylt0gxssw0xvmm",
    "balance": 3323333333333333,
    "kfiBalance": 7000000000000,
    "delegation": {
        "address": "klv14nwrysndqs29wvvskwk8lq3ch9v4jatf6l6mxa6vrj9wylt0gxssw0xvmm",
        "value": 10000000000000
    }
}
]

nodesSetup.json

In nodesSetup.json, we have some basic blockchain configurations and the initial list of nodes. First, we will delete the current configuration and write it according to our generated keys.

Ex:

{
    "startTime": 1720483200, // Genesis start time
    "slotInterval": 4000, // Time of a slot (block)
    "slotsPerEpoch": 20, // How many slot a epoch have.  slotInterval * slotsPerEpoch = EpochInterval
    "consensusGroupSize": 3, // Consensus Size 
    "minNodes": 3, // Min Nodes
    "chainID": "79652",
    "minTransactionVersion": 1,
    "klvDenomination": 6,
    "initialNodes": [ // Initial Nodes List
        {
            "pubkey": "807cf3168ec886165ac2128c77354bfd80004bfc0ac2a9f3a2b24f0805071a8bc352020338de993ca3bd85a4efe757124553ca9bc65de2c28f5450a42cbdc82bb7ad14857ddfea6577e6859de2d07e89ed663951ca55c6892deafc7c5f4a4807", // BLS
            "address": "klv1y6snwael5jpfgfzrf3wzlsddhwew93v2lf5wda6uz9u2ep9e8uvsehufg6" // Address
        },
    ]
}

Based on the data we generate, this is what the configuration will look like:

{
  "startTime": 1720477239,
  "slotInterval": 4000,
  "slotsPerEpoch": 20,
  "consensusGroupSize": 2,
  "minNodes": 2,
  "chainID": "4123",
  "minTransactionVersion": 1,
  "klvDenomination": 6,
  "initialNodes": [
    {
      "pubkey": "158449b4219fff1151d2e0eaf50c63cb95ba8b582ac2ff5429f90f500dd816068ac071c5a13490b65296b22a5fb0ab07359029a3fce0761441dd2c820713e52fc0a8ef3b9a0d8af28c9f2b74b5a5542956e481e596911b559b8c76cb66fa0685",
      "address": "klv1lay4f5657l535g7j5nx8wenhqvlhjune2zfvt94sv5kv7qk3snwqajpux3"
    },
    {
      "pubkey": "9eb5fe005d5ad50a621ac9728ed2dcaf780c1d69fae86399997d0b9bc3f53c1b968a56a048f083b60290ebd037267f12a2965220100e85324b3a68fd940574873b7bed6d6cdf66fa404f337e52e306010d8297f0d89c9b04e4514c7c7536d293",
      "address": "klv14nwrysndqs29wvvskwk8lq3ch9v4jatf6l6mxa6vrj9wylt0gxssw0xvmm"
    }
  ]
}

enableEpochs.yaml

Just use the configuration below so that the blockchain is the same as mainnet.

maxNodesChangeEnableEpoch:
  - epochEnable: 0
    nodesToShuffle: 4

enableEpochs:
  claimKFI: 0
  processorFlowITOPrice: 0
  fixStakingBuckets: 0
  kdaFpr: 0
  bigBucketsCompute: 0
  fprComputeAndKdaFeeFlow: 0
  fixDelegationSameEpoch: 0

config.yaml

Just change thresholdMinConnectedPeers to 0

external.yaml

Make sure the enabled config is false. if you want to use a node-indexer, you must change the node-indexer config to true.

Seed Node Config

To run a localnet with more than one validator, it will be necessary to configure the seed node so that the validators can communicate correctly.

  1. Create a folder to seednode config

Create a folder for seednode settings and create an empty file called config.yaml, the folder structure should look something like this.

    localchain
    ├── config
    │   ├── api.yaml
    │   ├── config.yaml
    │   ├── enableEpochs.yaml
    │   ├── external.yaml
    │   ├── genesis.json
    │   └── nodesSetup.json
    ├── node-0
    │   ├── validatorKey.pem
    │   └── walletKey.pem
    ├── node-1
    │   ├── validatorKey.pem
    │   └── walletKey.pem
    └── seednode
        └── config.yaml
  1. Config Seednode

Just use this configs for you seednode.

p2p:
  node:
    port: 37373-37373
    seed: beklever
    maximumExpectedPeerCount: 0
    thresholdMinConnectedPeers: 0
  kadDhtPeerDiscovery:
    enabled: true
    refreshIntervalInSec: 10
    protocolID: /klv/kad/0.0.1
    initialPeerList:
    bucketSize: 100
    routingTableRefreshIntervalInSec: 300
  sharding:
    targetPeerCount: 0
    maxIntraShardValidators: 0
    maxCrossShardValidators: 0
    maxIntraShardObservers: 0
    maxCrossShardObservers: 0
    type: NilListSharder
    
logs:
  LogFileLifeSpanInSec: 86400
marshalizer:
  type: protobuf
  1. P2P Node config

After you configure your seed node, you need to add one more change to your validator's config.yaml file.Change the initialPeerList setting to the one below

    initialPeerList:
      - /ip4/172.20.0.5/tcp/37373/p2p/16Uiu2HAkwUme4SM2uQSBydi8s3FhZDVqA3RbNQDEojzQGnE5bv4Z

Running the blockchain

Ensuring that all settings are correct. Now we can finally run our blockchain.

Create a docker-compose.yaml file and add the code below.

version: '3'
services:
  seednode:
    image: kleverapp/klever-go:latest
    container_name: seednode
    restart: unless-stopped
    volumes:
      - ./seednode:/opt/klever-blockchain/config/seednode
    entrypoint: /usr/local/bin/seednode
    command: [
      "--rest-api-interface=0.0.0.0:8799",
    ]
    networks:
      klever:
        ipv4_address: 172.20.0.5
  node0:
      image: kleverapp/klever-go:latest
      container_name: node0
      restart: unless-stopped
      networks:
        - klever
      volumes:
        - ./config:/opt/klever-blockchain/config/node
        - ./node-0/validatorKey.pem:/opt/klever-blockchain/config/validatorKey.pem
        - ./dbs/node-0:/opt/klever-blockchain/db
        - ./logs/node-0:/opt/klever-blockchain/logs
      entrypoint: /usr/local/bin/validator
      command: [
        "--log-level=*:INFO",
        "--use-log-view",
        "--validator-key-pem-file=./config/validatorKey.pem",
        "--rest-api-interface=0.0.0.0:8800"
      ]
  node1:
      image: kleverapp/klever-go:latest
      container_name: node1
      restart: unless-stopped
      networks:
        - klever
      volumes:
        - ./config:/opt/klever-blockchain/config/node
        - ./node-1/validatorKey.pem:/opt/klever-blockchain/config/validatorKey.pem
        - ./dbs/node-1:/opt/klever-blockchain/db
        - ./logs/node-1:/opt/klever-blockchain/logs
      entrypoint: /usr/local/bin/validator
      command: [
        "--log-level=*:INFO",
        "--use-log-view",
        "--validator-key-pem-file=./config/validatorKey.pem",
        "--rest-api-interface=0.0.0.0:8801"
      ]
networks:
  klever:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

You can now run the blockchain on your machine using the command below.

docker-compose up -d

Was this page helpful?