What you might not know about VeChainThor yet (Part V) — Customizing Your Own VeChainThor

Peter Zhou
Sep 5 · 5 min read

This is the fifth article of the “What you might not know about VeChainThor yet” series. You can find the link of the previous article at the end.

VeChainThor allows users to customize and deploy their own blockchain networks. The rest of this article consists of two parts. The first part focuses on customization while the second part provides an example of deploying a customized VeChainThor.


You can customize VeChainThor through configuring a JSON file and passing it to the Thor client when you launch a node. The configuration file tells the system your settings of global parameters, authority masternodes and on-chain governance, how you would like to mint and allocate VET and VTHO tokens, and any contract(s) you want to deploy from the beginning. These choices result in the creation of the genesis block that is unique to your customized VeChainThor.

A sample configuration file is shipped with the source code and can be found at <THOR_DIR>/genesis/example.json. The following are the values you can tweak in the file:

  • launchTime: Unix timestamp (in second) | Earliest launch time of your customized blockchain.
  • gasLimit: Unsigned integer | Gas limit of the genesis block.
  • extraData: String (length ≤ 28) | Any words you want to put in the genesis block.
  • accounts: account[]| Account(s) to be created for minting and allocating tokens or deploying contract(s).
  • account.address: 20-byte hex-string | Account address.
  • account.balance: Unsigned integer | VET balance in Wei.
  • account.energy: Unsigned integer | VTHO balance in Wei (optional).
  • account.code: hex-string | EVM bytecode of a contract account (optional).
  • account.code: hex-string | EVM bytecode of a contract account (optional).
  • account.storage: key-value pairs with each key and value being a 32-byte hex-string | Storage values of a contract account (optional).
  • authority: masternode[]| List of authority masternodes.
  • masternode.masterAddress: 20-byte hex-string | Master account address.
  • masternode.endorsorAddress: 20-byte hex-string | Address of the account that holds the required VET deposit.
  • masternode.identity: 32-byte hex-string | Masternode identity information.
  • params.rewardRatio: Unsigned integer ∈ [0, 1e+18] | VTHO reward ratio = params.rewardRatio / 1e18.
  • params.baseGasPrice: Unsigned integer | Base gas price value.
  • params.executorAddress: 20-byte hex-string | Address of the executor contract account.
  • executor.approvers: approver[] | Accounts authorized to approve on-chain governance actions. For instance, the approvers defined for the mainnet represent the steering committee members of the VeChain Foundation.
  • approver.address: 20-byte hex-string | Approver account address.
  • approver.identity: 32-byte hex-string | Approver identity information.

My demo code for generating a valid JSON file can be found here.

Local Deployment

After generating the JSON file, you are ready to deploy your own customized VeChainThor. You need to have at least two nodes up and syncing with each other. Moreover, at least one of the nodes has to be a masternode registered in the JSON file.

If you are to run nodes on machines with a public IP address, you just need to start the Thor client by the command

bin/thor --network <JSON_FILE>

If it was not the case, you would need to use other options of the thor command for deployment. As an example, I will show you step by step how to deploy a customized network that consists of two nodes running locally on a single machine.


  • N1 and N2: node names.
  • <CONFIG_DIR1> and <CONFIG_DIR1>: directories for storing the master account private keys for N1 and N2.
  • <DATA_DIR1> and <DATA_DIR1>:: directories for storing ledger data for N1 and N2.
  • <ENODE1>: substring of the node ID of N1 that begins with "enode://" and ends right before "@[extip]".

Step 1

Let N1 be a masternode. We first obtain its master account address via:

thor master-key --config-dir <CONFIG_DIR1>

Note that the master account address would be different if you used different --config-dir values.

After that, we need to register the master node in the JSON file. In my demo code, it is done by:

const masterAddress1 = '0x929710d206f0e1133f353553353de5bc80c8460b'; ... 
const endorsor = '0x5e4abda5cced44f70c9d2e1be4fda08c4291945b';
const _authority: Authority[] = [
{ masterAddress: masterAddress1, endorsorAddress: endorsor, identity: strToHexStr('id1', 64) },

We also need to make sure that we allocate sufficient fund to the endorsor account to meet the masternode deposit requirement (defined by params.proposerEndorsement). The following code creates the account with the required amount of VET tokens.

const _accounts: Account[] = [ 
address: endorsor,
balance: 25000000000000000000000000,

Note that besides the VET balance, I also set the optional values for the endorsor account. It is purely for the purpose of demonstrating that you can create a contract account in the JSON file.

Step 2

Launch N1 by the following command:

thor --network <JSON_FILE> \ 
--config-dir <CONFIG_DIR1> \
--data-dir <DATA_DIR1>

where we use options --config-dir and --data-dir to designate directories for storing keys and ledger data, respectively.

From the above information, we get <ENODE1> = enode://5e61...67fb.

Step 3

Since N1 and N2 are running on the same machine and N1 has used the default API portal (http://localhost:8669) and P2P port number (11235), we need to set new values for them via options --api-port and --p2p-port when launching N2. The following is the command for launching N2:

thor --network <JSON_FILE> \ 
--config-dir <CONFIG_DIR2> \
--data-dir <DATA_DIR2> \
--api-addr localhost:8670 \
--p2p-port 11236 \
--bootnode <ENODE1>@

Option --bootnode passes information of the boot node from which the current node can sync with others within the same blockchain network. The screenshots below showed the information output for N1 and then N2. It can be seen that they were synced with each other and new blocks generated to extend the chain.


Now, the customized VeChainThor is up and running. We can use tools such as Connex REPL to interact with the blockchain using Connex interface.

After installing Connex REPL, type the following command to connect to N2:


To check the genesis block, you can type


To get the balance and code of the endorsor account, you can type

await thor.account('<ENDORSOR_ADDR>').get() 
await thor.account('<ENDORSOR_ADDR>').getCode()

You can then compare the output values with those defined in the JSON file for the validation purpose.

Previous Articles

What you might not know about VeChainThor yet (Part I) — Transaction Uniqueness

What you might not know about VeChainThor yet (Part II) — Forcible Transaction Dependency

What you might not know about VeChainThor yet (Part III) — Transaction Fee Delegation (VIP-191)

What you might not know about VeChainThor yet (Part IV) — Mining gas price

Originally published at https://bbs.vechainworld.io on September 5, 2019.

Peter Zhou

Written by

Chief Scientist@VeChain, Head of Research & IP

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade