Adding an External Module¶
Introduction¶
Developers building on top of the Templates offered by Tanssi might want to add some external modules/dependencies into their runtime to expand certain functionality.
The Tanssi repository and the templates take all the dependencies from a fork of the official Polkadot SDK repository. This fork is maintained by the Tanssi engineering team, which usually contributes actively to Substrate development by fixing issues and enhancing functionalities, and, as a result, the fork repository frequently stays temporarily ahead of the official one.
A double reference issue may arise when adding an external dependency, such as a pallet from a third party. This happens if a Tanssi module references a dependency from the Polkadot SDK fork repository, and the third party references the same dependency from the official Polkadot SDK repository. To solve this issue, the references to the dependencies must be unified.
Solving Dependencies Conflicts with Diener¶
To efficiently handle the dependencies and their origins, you can check out the tool diener.
If the diener
executable file, the cloned Polkadot SDK repository, and your Tanssi fork are located in the same folder, step into the Tanssi fork folder and execute the following command:
../diener patch --crates-to-patch ../polkadot-sdk \
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch tanssi-polkadot-v1.11.0
This command applies the changes to the Cargo.toml
file, patching the dependencies, and solving the double reference issues.
You can visit the diener documentation to learn more about the tool and other extra functions it offers.
Example of the Double Reference Issue¶
To illustrate the situation, the following steps add a demo external module to a custom runtime based on the baseline Tanssi appchain template. One way to follow this tutorial is to clone the Tanssi Github repository, which will act as the root repository of the project.
This tutorial will generate a multiple reference compile-time error. Finally, the steps will show you how to fix the compile error by patching the dependencies with the tool diener
, the runtime will compile successfully and work as intended.
Add a Third-Party Dependency¶
Similarly to what is described in the built-in module article, adding a third-party module requires the following steps:
- Declare the dependency in the root
Cargo.toml
file - Make the standard features available to the compiler
- Configure and add the module to the runtime
Should the third-party module reference any dependency already referenced from a distinct source or version, compilation will fail.
The following diagram shows how two different references to the same dependency are being included in the runtime, causing the compilation to fail:
To resolve this issue, it will be necessary to apply a patch so that the references for the dependency are unified:
Declaring the Dependency¶
The first step to reproduce the double reference issue is to declare the dependency in the Cargo.toml
file located in the repository's root folder, under the section [dependencies]
. For this example, a simple toggle module is used.
This toggle
module, built for testing and educational purposes, adds basic logic to the runtime, allowing users to switch a state between true and false.
[dependencies]
...
pallet-toggle = {
git = "https://github.com/papermoonio/pallet-toggle.git",
default-features = false
}
...
Make the Standard Features Available to the Compiler¶
Having declared the module in the workspace Cargo.toml
file, the dependency can now be added to the specific template Cargo.toml
file, which, for this example that uses the Tanssi GitHub repo, is located in the folder container-chains/templates/simple/runtime
.
[dependencies]
...
pallet-toggle = { workspace = true }
...
In the same Cargo.toml
file, add the following features.
[features]
default = [
"std",
]
std = [
...,
"pallet-toggle/std",
...
]
...
runtime-benchmarks = [
...,
"pallet-toggle/runtime-benchmarks",
]
try-runtime = [
...,
"pallet-toggle/try-runtime",
]
Configure and Add the Module to the Runtime¶
Next, add the following snippet to the lib.rs
file inside the runtime folder. This configures the module and adds the module within the construct_runtime!
macro.
...
impl pallet_template::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_template::weights::SubstrateWeight<Runtime>;
}
construct_runtime!(
pub enum Runtime
{
...
...
Toggle: pallet_toggle,
}
);
Compile Runtime¶
After completing the preceding steps, the module is declared a dependency in the project, configured, and added to the runtime.
Compile the template using the following command:
cargo build -p container-chain-simple-node --release
The terminal output will display an error, similar to the following, caused by different modules referencing different versions of the same dependency:
error: failed to select a version for `syn`.
Patch Dependencies¶
Finally, executing the diener
command will add a patch
section to your workspace Cargo.toml
file, overriding the dependencies and unifying origins and versions.
This is what the execution in the terminal looks like:
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch tanssi-polkadot-v1.3.0
[2024-01-10T23:26:27Z INFO diener] Running diener v0.4.7
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bridge-runtime-common.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-header-chain'
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-runtime'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core-hashing'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api-proc-macro' .
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-debug-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-externalities'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-std'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-storage'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-runtime-interface'.
As shown in the terminal output, diener
adds a patch for the dependencies, creating a patch
section in your toml
overriding their origin:
[patch."https://github.com/paritytech/polkadot-sdk"]
bridge-runtime-common = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-header-chain = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
...
Finally, compiling will succeed, and the module will be built into your runtime.
| Created: December 1, 2023