Setting up a relayer node

On this page, you’ll learn:

  • How to set up a relayer node on a sidechain?

  • How to set up a relayer node on a mainchain?

The process of setting up a relayer node on a mainchain and sidechain are almost identical. The subsequent sections describe the steps to set up a relayer node.

Prerequisites

To use this guide, it is assumed that the following criteria have been met:

  • An up-and-running node connected to the mainchain exists, see How to set up a Lisk Mainnet node.

  • An up-and-running node connected to the sidechain exists.

  • The sidechain must be registered to the mainchain as described in the How to register a sidechain guide.

  • The sidechain account on the mainchain must have either the registered or active status.

1. Installing the Chain Connector plugin

The chain connector plugin comes pre-installed with any chain that has been initialized with Lisk SDK v6. However, in case it is required to install it manually, run the following command in the root folder of the client.

npm install @liskhq/lisk-framework-chain-connector-plugin

2. Configuring the Chain Connector plugin

Once the plugin is installed, various configurations are available for the node operator to set. With these configurations, the details such as the receiving chain’s chainID and its IPC path or WS URL, CCU frequency, encrypted private key, etc. can be set.

The following table describes the available configuration options.

  • Configuration options

  • Sample configuration

Name Type Description Sample

receivingChainIPCPath

string

The IPC path of a receiving node

~/.lisk/lisk-core

receivingChainWsURL

string

The WS URL of a receiving node

ws://142.93.230.246:4002/rpc-ws

ccuFrequency

integer

Number of blocks after which a CCU should be created

1

encryptedPrivateKey

string

Encrypted privateKey of the relayer

"kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=da36ef64cbf2687d62b014dafdfa8ef8c823b2b1562ae78819599080e4500529b75e80093fba066879f0767e0de83abe285efb259dd9be5109b8a4ef66cfc52ec613314586c1aa1da3a6737c0f8b7f0de7fb4d1b85860cd23915bbcee774e1d85b357e342816a917e517f7c702e1a1deb28dd69a4b69ae2ac67a5c4c4236101c&mac=e253decce05dd50758400d5c7408532a162fedf583ff9cafcb7ad3e12f6b8011&salt=40ab2dcdd387e4372ed1dbb948a9ef84&iv=4559dcaee67eb2c1a0957ecf&tag=81c2c332d915454bed4be26018c598c5&iterations=1&parallelism=4&memorySize=2024"

ccuFee

string

The fee to be paid for each CCU transaction. It’s an optional value and is only used if the user wants to pay a fee higher than the minimum calculated fee. In that case, the minimum calculated fee is overridden with the value passed with the ccuFee property. If the user-provided fee is lower than the calculated minimum fee then the calculated minimum fee will be used in that case.

100000000

isSaveCCU

boolean

Flag for the user to either save or send a CCU on creation. Sending a CCU is the default option.

false

ccuSaveLimit

integer

Number of CCUs to save.

300

maxCCUSize

integer

Maximum size of CCU to be allowed

50

registrationHeight

integer

Height at the time of registration on the receiving chain.

100

receivingChainID

string

Chain ID of the receiving chain.

04000000

{
    // [...]
    "plugins": {
        "chainConnector": {
            "encryptedPrivateKey": "kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=da36ef64cbf2687d62b014dafdfa8ef8c823b2b1562ae78819599080e4500529b75e80093fba066879f0767e0de83abe285efb259dd9be5109b8a4ef66cfc52ec613314586c1aa1da3a6737c0f8b7f0de7fb4d1b85860cd23915bbcee774e1d85b357e342816a917e517f7c702e1a1deb28dd69a4b69ae2ac67a5c4c4236101c&mac=e253decce05dd50758400d5c7408532a162fedf583ff9cafcb7ad3e12f6b8011&salt=40ab2dcdd387e4372ed1dbb948a9ef84&iv=4559dcaee67eb2c1a0957ecf&tag=81c2c332d915454bed4be26018c598c5&iterations=1&parallelism=4&memorySize=2024",
            "ccuFee": "800000",
            "receivingChainIPCPath": "~/.lisk/lisk-core",
            "receivingChainID": "04000000"
        }
    }
}
A node operator must add either the value of receivingChainIPCPath or receivingChainWsURL in the chain connector’s config.

3. Enabling the Chain Connector plugin

A node operator can perform the following steps to enable the chain connector plugin and turn a node into a relayer node.

3.1. Creating an encrypted private key

  1. The first step is to create an encrypted private key. A node operator can use a REPL session to call the Lisk cryptography libraries.

    • Sidechain

    • Mainchain

    ./bin/run console
    Entering Lisk REPL: type `Ctrl+C` or `.exit` to exit
    lisk-core console
    Entering Lisk REPL: type `Ctrl+C` or `.exit` to exit
  2. The encryptedPrivateKey can be created by calling the encryptMessageWithPassword function. It accepts two arguments: a private key of the account which is supposed to be used as a relayer address and a password.

    The account should have sufficient balance so that the encrypted private key can be used for signing and sending the transaction.

    • Sidechain

    • Mainchain

    Creating an encrypted key on a sidechain node
    sidechain_client> lisk.cryptography.encrypt.stringifyEncryptedMessage(await lisk.cryptography.encrypt.encryptMessageWithPassword('0d7501d3d5c9accaefb3c0b6a569473b59391ae406f6324f98fa6dd70e119368a6454f898d3b82c41b158206c72ecfe917a1071c8084b496a0c5867afc10830b', 'lisk'))
    Encrypted key
    'kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=57db80457db93a1abeceee5c6f951ca04579c447a06f45cf5e8b5398e207a26da53a6b191a02c479ede455950eacb48f32d6609f2cd4b5a1a51e895b210b587ef046e6c3151ef2212efd0808b45328742d09a279e7d667f1670ff02a2fd5c91f4afd0a08efb8e6e90b0b11e93b15da8daaeea543a0ff54f3dd51c66cac3b04c6&mac=7822258b12e0c787f5bd622c562914438a9d74ca1e11e11b840f3001a678b04f&salt=d4d051a123326ad2b82c022603e790b6&iv=0bb9e76cd5163d6c5af9d89d&tag=fbcdb355b5135d48df948841de5fcdf5&iterations=1&parallelism=4&memorySize=2024'
    Creating an encrypted key on a mainchain node
    lisk-core> lisk.cryptography.encrypt.stringifyEncryptedMessage(await lisk.cryptography.encrypt.encryptMessageWithPassword('0d7501d3d5c9accaefb3c0b6a569473b59391ae406f6324f98fa6dd70e119368a6454f898d3b82c41b158206c72ecfe917a1071c8084b496a0c5867afc10830b', 'lisk'))
    Encrypted key
    'kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=f4dd49061a128d06184308a235311dc487737b7c4a688409224ed39b7d8e76a6cdd814500dd7221297ed122d277af8ba46d42ebd340d228fe6c77132543b303c97ab89e151ecd9f2739284c60c66ab68c0f3531ffc6cbdedad2acc431e8d8e48dffd7c7eda3dfe5f404e00ef7ae825d34da7787bf792b6ecb84ea1bfe10e9ca6&mac=363141e645d5564a150a2634060bd273276b0c987a65cf64513a7871565c3f2a&salt=93213d2d1c11e91d64771c173f8bf3c1&iv=0132fa14a4ed289deb07ee11&tag=7b64ed4a0453302d54bba29d4f7a68ea&iterations=1&parallelism=4&memorySize=2024'

    The encryptMessageWithPassword function will return an encrypted key, which should be added to the config of the blockchain.

3.2. Prepare config for chain connector plugin

Each node whether a mainchain or a sidechain expects mandatory configurations as shown in the following snippets.

  • Sidechain

  • Mainchain

"chainConnector": {
    "encryptedPrivateKey": "kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=57db80457db93a1abeceee5c6f951ca04579c447a06f45cf5e8b5398e207a26da53a6b191a02c479ede455950eacb48f32d6609f2cd4b5a1a51e895b210b587ef046e6c3151ef2212efd0808b45328742d09a279e7d667f1670ff02a2fd5c91f4afd0a08efb8e6e90b0b11e93b15da8daaeea543a0ff54f3dd51c66cac3b04c6&mac=7822258b12e0c787f5bd622c562914438a9d74ca1e11e11b840f3001a678b04f&salt=d4d051a123326ad2b82c022603e790b6&iv=0bb9e76cd5163d6c5af9d89d&tag=fbcdb355b5135d48df948841de5fcdf5&iterations=1&parallelism=4&memorySize=2024",
    "ccuFee": "800000",
    "receivingChainIPCPath": "~/.lisk/lisk-core"
    "receivingChainID": "04000000"
}
"chainConnector": {
    "encryptedPrivateKey": "kdf=argon2id&cipher=aes-256-gcm&version=1&ciphertext=f4dd49061a128d06184308a235311dc487737b7c4a688409224ed39b7d8e76a6cdd814500dd7221297ed122d277af8ba46d42ebd340d228fe6c77132543b303c97ab89e151ecd9f2739284c60c66ab68c0f3531ffc6cbdedad2acc431e8d8e48dffd7c7eda3dfe5f404e00ef7ae825d34da7787bf792b6ecb84ea1bfe10e9ca6&mac=363141e645d5564a150a2634060bd273276b0c987a65cf64513a7871565c3f2a&salt=93213d2d1c11e91d64771c173f8bf3c1&iv=0132fa14a4ed289deb07ee11&tag=7b64ed4a0453302d54bba29d4f7a68ea&iterations=1&parallelism=4&memorySize=2024",
    "ccuFee": "800000",
    "receivingChainIPCPath": "~/.lisk/relayer",
    "receivingChainID": "04000002"
}

Once the configuration is ready, update the config.json file of the respective client such as mainchain or sidechain.

3.3. Register plugin

Once the config.json has been updated, it is required to register the plugin with the client. The process differs for both sidechain and mainchain.

  • Sidechain

  • Mainchain

On a sidechain, the plugin can be enabled using the --enable-chain-connector-plugin flag whilst starting the blockchain client.

./bin/run start --enable-chain-connector-plugin --overwrite-config

Alternatively, the plugins.ts file of the client can be updated to have the following options:

sidechain_client/src/app/plugins.ts
import { Application } from 'lisk-sdk';
// Import the 'ChainConnectorPlugin'
import { ChainConnectorPlugin } from '@liskhq/lisk-framework-chain-connector-plugin';

export const registerPlugins = (app: Application): void => {
    // Register the ChainConnectorPlugin with the app
    app.registerPlugin(new ChainConnectorPlugin());
};

The client must be rebuilt to entertain the changes to the code.

npm run build

The client can then be started with the following command:

./bin/run start --overwrite-config

On a mainchain, the plugin can be enabled using the --enable-chain-connector-plugin flag whilst starting the blockchain client.

lisk-core start --network alphanet --enable-chain-connector-plugin --overwrite-config

Since the config of the chain is updated during the process, the node operator must update the existing config with the --overwrite-config flag.

3.4. Result

Once the client is running, the node operators should see the following log messages, depending on the type of node.

  • Sidechain

  • Mainchain

2023-03-17T14:42:30.426Z INFO XYZ.local application 96733 No valid CCU can be generated for the height: 58
2023-03-17T14:42:30.426Z INFO XYZ.local plugin_chainConnector 96899 No valid CCU can be generated for the height: 58

Since we just set up a relayer node and haven’t sent a CCU/CCM, the aforementioned log messages are expected.

The messages suggest that the blockchain doesn’t have any finalized block height for which we can create a certificate, or there are no pending CCUs/CCMs to send across the chain.

The relayer node will start relaying CCUs to the receiving chain, once the finalized height is reached.

Once the plugin is enabled, it is essential to invoke the chainConnector_authorize endpoint to authorize the signing and sending of CCUs.

A node can also output various warnings or error messages as log output; the details of which can be seen in the following tables:

Table 1. Various log messages and their descriptions
Log message Description

Warnings messages: The following log messages can be considered as warnings and they don’t require restarting a node or plugin to fix the underlying issue as such messages usually suggest problems with chain registration or apiClient.

Sending chain is not registered to the receiving chain yet and has no chain data.

As the log suggests, the sending chain is not registered yet on the receiving chain but at the same time, all the data related to CCU creation is saved such as blockheaders, aggregateCommits, events containing CCM creation, proof of outboxRootWitness and validatorsInfo, etc. For more information read LIP 53.

No attempt to create CCU either due to ccuFrequency or the node is syncing

In this scenario, the data required for CCU creation is saved and only CCU creation is delayed due to the ccuFrequency. The user has to wait till the end of node syncing to proceed further.

Receiving chain is not registered yet on the sending chain.

In this case, the plugin is running fine and saving data for CCU creation, however, it cannot yet create or send CCU as the user has not yet registered the receiving chain on their own chain. For more information, read LIP 43.

CCU cant be created as there are no pending CCMs for the last certificate

If no new certificate has been created since the last certificate and no pending CCMs are left then, no CCU can be created and the user needs to wait for the next certificate height on its sending chain.

Not able to connect to receivingChainAPIClient. Trying again on next new block.

In this case, the plugin will try again on the next new block if the API issue is resolved to create/send a CCU.

Error occurred while submitting CCU for the blockHeader at height:

The log message suggests that the CCU was created successfully but its submission failed for some reason. In this case, the user needs to check the error attached to the log to debug further.

Error occurred while accessing receivingChainAPIClient but all data is saved on newBlock.

This error occurs when receivingChainAPIClient is not working, the user needs to check the receivingChainAPIClient or receivingChain node if they are working properly or not once they see this error within the logs. For example, receivingChainWsURL or receivingChainIPCPath provided by a user could be incorrect. In this case, all the data is saved required for CCU creation which means when the user fixes apiClient, the plugin would still be able to create/send CCUs.

Error messages: The following error-related logs should be fixed by the user and require a node restart. In these cases, the plugin is usually missing the data required to create CCUs, and that usually happens when something goes wrong while saving data on the plugin. Such errors also occur when the user enables the config.registrationHeight or the height after the height from where the plugin started looking for certificates.

No block header found for the last certified height ${lastCertificate.height}.

In this case, the plugin is looking at a different height and when it is unable to find a certificate, it logs out the aforementioned error.

No validatorsHash preimage data present for the given validatorsHash

As the error suggests, the node is unable to retrieve the validatorHash preimage at the given height.

No validators data found for the given validatorsHash

If the node is unable to find data based on the given validatorsHash on the given height, it will log the aforementioned error.

No validators data found for the given last certificate height

If the node is unable to find data based on the given last certificate height, it will log the aforementioned error.

Possible ways to debug the case when the first CCU is not sent and the user sees the aforementioned errors are described below:

  • Reset the node and start syncing from the height before config.registrationHeight.

  • Try to give a higher value to the config.registrationHeight property from where the plugin started storing data correctly, assuming that the chain of trust is maintained.

Sometimes if the node is very busy with syncing over a long period of time, the plugin misses out on storing information from the new block event, which might lead to the aforementioned errors. This should not usually happen, however, if it does then, the user must re-sync their node.