Update a dapp chain
Dapps evolve over time. This section discusses how to update a dapp chain to a new version seamlessly.
There are two approaches to updating a chain:
- Specify different config entry points for old and new versions, or
- Reference compiled configuration of the old version
Project structure before the update
We use an example project with a basic ft3 structure for both approaches
You have all the chain's code within the rell/src
folder:
rell/src
├── lib
│ ├── ft3
│ └── … (other optional libraries)
└── module.rell
And config.template.xml
must look like the following:
<run wipe-db="true">
<nodes>
<config src="../../node-config.properties" add-signers="true" />
</nodes>
<chains>
<chain name="YOUR_CHAIN_NAME" iid="0">
<config height="0">
<app module="">
<args module="lib.ft3.core">
<!-- ... (your chain configs) -->
</args>
</app>
</config>
</chain>
</chains>
</run>
Update by specifying different entry points
Use this approach to have a clear overview of the old version's code.
An example project to demonstrate this approach is available at branch
example/update-chain-keep-old-code
of develop-chromia
:
git clone https://bitbucket.org/chromawallet/develop-chromia.git
git checkout example/update-chain-keep-old-code
Restructure the code
As mentioned earlier, this approach's target is to have a clear overview of old code after several updates. Creating different entry modules for each chain version is an excellent procedure.
We name each module with the current version tag so that it's clear what's run in the past. After that, we need to update the config.template.xml
file by adding each version configuration and the height at which the changes become effective.
The Rell structure then looks like the following:
rell/src
└── v0_0_1
├── lib
│ ├── ft3
│ └── … (other optional libraries)
└── module.rell
So essentially, we just created a folder v0_0_1
inside src
and moved all the previous files there.
We need to update the config.template.xml
file to reflect this
change. Note the change at <app>
and <arg>
tags:
<run wipe-db="true">
<nodes>
<config src="../../node-config.properties" add-signers="true" />
</nodes>
<chains>
<chain name="YOUR_CHAIN_NAME" iid="0">
<config height="0">
<app module="v0_0_1">
<args module="v0_0_1.lib.ft3.core">
<!-- ... (your chain configs) -->
</args>
</app>
</config>
</chain>
</chains>
</run>
As a matter of information, the project's entry path is available under postchain/config/nodes/dev/blockchains/app/entry-file.txt.
Confirm the new configuration is working properly by starting the chain:
./postchain/bin/run-node.sh dev
Add new code
Now that you've restructured the code, you can upgrade the code. To do
that, you must copy the v0_0_1
folder and rename it as v0_0_2
.
rell/src
├── v0_0_1
│ ├── lib
│ │ ├── ft3
│ │ └── … (other optional libraries)
│ └── module.rell
│
└── v0_0_2
├── lib
│ ├── ft3
│ └── … (other optional libraries)
└── module.rell
For easy testing, you can add one new query in the module.rell
file of
v0_0_2
:
module;
import lf: .lib.ft3.ft3_basic_dev;
query get_version() {
return "0.0.2";
}
If everything works correctly, you need not receive any response when
running the module v0_0_1
but you receive 0_0_2
when running the
module v0_0_2
.
Set the migration to a new module
To migrate to the new module, you need to update the
config.template.xml
file.
You add a new <config>
tag inside the <chain>
, and enable at a certain (block) height
:
<run wipe-db="true">
<nodes>
<config src="../../node-config.properties" add-signers="true" />
</nodes>
<chains>
<chain name="YOUR_CHAIN_NAME" iid="0">
<config height="0">
<app module="v0_0_1">
<args module="v0_0_1.lib.ft3.core">
<!-- ... (your chain configs) -->
</args>
</app>
</config>
<config height="20">
<app module="v0_0_2">
<args module="v0_0_2.lib.ft3.core">
<!-- ... (your chain configs) -->
</args>
</app>
</config>
</chain>
</chains>
</run>
In this example, after 20 blocks, the chain changes app module to
v0_0_2
.
Ensure that the block height for the new configuration is safely higher than the current height. Setting a block height lower than the current height might cause problems for nodes that want to sync by re-applying all the transactions.
Test the chain
Start the chain with
./postchain/bin/run-node.sh dev
Recall the new query get_version()
we created earlier. We can test the
chain's version with a simple script such as:
const pcl = require("postchain-client");
const node_api_url = "http://localhost:YOUR_NODE_PORT";
// default blockchain identifier used for testing
const blockchainRID = YOUR_NODE_BRID;
const rest = pcl.restClient.createRestClient(node_api_url, blockchainRID, 5);
const gtvHash = pcl.gtv.gtvHash;
const gtx = pcl.gtxClient.createClient(
rest,
Buffer.from(blockchainRID, "hex"),
[]
);
const gtv = pcl.gtv;
const gtxU = pcl.gtx;
(async () => {
try {
const version = await gtx.query("get_version", {});
} catch (e) {
console.log("New version not yet implemented");
} finally {
if (version) {
console.log(`New version running: ${version}`);
}
}
})();
We can monitor the blockchain by linking it to the Explorer :
At block 20, you start getting a result back from the chain. It returns something like this:
POST URL http://localhost:7743/query/A54320E7D063AB94513467806FE800A1B95B26BF65C4F11D30C5D65859E4025C
0.0.2
Update by referencing the old configuration
You can use the second approach to specify previously compiled configurations in the config tag.
Use this approach if you want a cleaner code structure.
An example project to demonstrate this approach is available at branch
feature/CCD210-update-chain-tutorial
of develop-chromia
:
git clone https://bitbucket.org/chromawallet/develop-chromia.git
git checkout feature/CCD210-update-chain-tutorial
What does run-node.sh do?
To discuss this approach, we must first explain what happened when you execute ./postchain/bin/run-node.sh dev
in the console.
This command does three things.
- First, it creates a target folder where the compiled blockchain configuration is available, something like:
mkdir -p ./postchain/runtime/nodes/dev
- Then it generates the blockchain configuration by compiling the Rell
code with the
config.template.xml
file.
The BRID is also generated at this step if it's a new chain (started with the -W
option). You don't want the BRID to change, so you must not use the -W
option while updating the chain.
./postchain/lib/multigen.sh ./postchain/config/nodes/dev/blockchains/app/config.template.xml -d ./rell/src -o ./postchain/runtime/nodes/dev/
It should create the following architecture inside
postchain/runtime/nodes/dev
:
postchain/runtime/nodes/dev
├── blockchains
│ └── 0
│ ├── 0.gtv (creates a one file rell module)
│ ├── 0.xml (creates the configuration that will be run, also by using the just created 0.gtv)
│ └── brid.txt (just a file where is output the brid of the chain, n.b. it will remain constant nevertheless the updates)
│
├── node-config.properties
└── private.properties
- Finally, it runs the new configuration with
./postchain/lib/postchain.sh run-node-auto -d ./postchain/runtime/nodes/dev
Backup old configuration
Compile the current code (by executing ./postchain/bin/run-node.sh dev
or only steps 1 and 2 described above).
In the generated configuration folder, copy runtime/nodes/dev/0.xml
into postchain/config/nodes/dev/blockchains/app
, and rename it
0.0.1.xml
.
Adding new codes
In the previous approach, you kept track from a code point of view of previous versions. You don't have to do that this time, so you can change the code directly without copying anything.
Add the following query in the module.rell
file:
module;
import lf: .lib.ft3.ft3_basic_dev;
query get_version() {
return "0.0.2";
}
Set the migration to a new module
Update the config.template.xml
file to config the migration.
Note that, unlike the other approach, this time, you don't have to change the <app>
and <args>
tags:
<run wipe-db="true">
<nodes>
<config src="../../node-config.properties" add-signers="true" />
</nodes>
<chains>
<chain name="YOUR_CHAIN_NAME" iid="0">
<config height="0">
<gtv src="0.0.1.xml"/>
</config>
<config height="20">
<app module="">
<args module="lib.ft3.core">
<!-- ... (your chain configs) -->
</args>
</app>
</config>
</chain>
</chains>
</run>
Ensure that the block height for the new configuration is safely higher than the current height. Setting a block height lower than the current height might cause problems for nodes that want to sync by re-applying all the transactions.
Compile the new configuration and start the chain:
./postchain/bin/run-node.sh dev
We can now Test the chain as before. If you try to
query for get_version()
after block 20, you must see 0.0.2
.