Rise In Logo





Polkadot Fundamentals and Substrate Development

Authorizing nodes in a network

We’re taking things forward in this chapter and building upon what we learnt in the previous one. In the previous chapter, we learnt how to build a simple network but with a known set of validator nodes. In this chapter, we will build an entire permissioned network of nodes but it will be a simplified version of how a permissioned network works in the real world.

What’s exciting about this chapter is that we will learn how to use a pre-built FRAME pallet and this is a great skill to have in case you plan to customize the behavior of your blockchain.

Node Authorization and Ownership

The node-authorization pallet is a prebuilt FRAME pallet that enables you to manage a set of nodes for a network. Each node is identified by a peer identifier (PeerId). Each peer identifier is owned by an account owner (AccountId) that claims the node.

There are two ways you can authorize a node to join the network:

  • By adding the peer identifier to the list of predefined nodes in the chain specification file.
  • By asking for a paired peer connection from a specific node.

You will be clear with this process by the end of this tutorial. 

Building the node template

To get started, we need to build the node template. Now we have done this step in the tutorials before but just so that we don’t miss it, it’s re-iterated here as well.

Change directory into where you had cloned the node template, switch to a new branch and build the node, below are the commands for reference - 

cd substrate-node-template
git switch -c my-wip-branch
cargo build --release

Adding the node authorization pallet

Now we need to add the authorization pallet to our node, so this is a critical step.

The step will be similar each time you want to add a new pallet to your project.

Now, before we can add a new pallet, we must add some info about it to our config file.

Open the runtime/cargo.toml file and locate the [dependencies] section and add the pallet-node-authorization crate to it.

It should look like this - 

[dependencies]
pallet-node-authorization = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" }

Now add the pallet-node-authorization/std to the features list to enable when compiling the runtime. It should look like this - 

[features]
default = ['std']
std = [
 ...
 "pallet-node-authorization/std",  # add this line
 ...
]

Now we will check whether the dependencies resolve correctly - 

cargo check -p node-template-runtime --release

You can simulate governance as well with the help of EnsureRoot privileged function that can be called using the sudo pallet. 

The Sudo pallet is included in the node template by default and enables you to make calls through the root-level administrative account.

To enable the EnsureRoot rule in your runtime:

Open the runtime/src/lib.rs file in a text editor.

use frame_system::EnsureRoot;

Implementing the config trait

Since we have added the node-authorization pallet, we will have to add some pallet-specific code in the config trait. To do this, open the runtime/src/lib.rs

In a file editor.

Add the parameter_types section for the pallet using the following code:

parameter_types! {
 pub const MaxWellKnownNodes: u32 = 8;
 pub const MaxPeerIdLength: u32 = 128;
}

Add the impl section for the config trait for the pallet - 

impl pallet_node_authorization::Config for Runtime {
 type RuntimeEvent = RuntimeEvent;
 type MaxWellKnownNodes = MaxWellKnownNodes;
 type MaxPeerIdLength = MaxPeerIdLength;
 type AddOrigin = EnsureRoot<AccountId>;
 type RemoveOrigin = EnsureRoot<AccountId>;
 type SwapOrigin = EnsureRoot<AccountId>;
 type ResetOrigin = EnsureRoot<AccountId>;
 type WeightInfo = ();
}

Add the pallet to the contrcut_runtime macro -

construct_runtime!(
pub enum Runtime where
   Block = Block,
   NodeBlock = opaque::Block,
   UncheckedExtrinsic = UncheckedExtrinsic
 {
   /*** Add This Line ***/
   NodeAuthorization: pallet_node_authorization::{Pallet, Call, Storage, Event<T>, Config<T>},
 }
);

Save the changes and close the file.

Now check if the configuration can compile -

cargo check -p node-template-runtime --release

Now before we can launch the network to use node authorization, some additional configuration is required.

Open node/cargo.toml in a text editor.

Locate the dependencies section and add this - 

[dependencies]
bs58 = { version = "0.4.0" }

Now open node/src/chain_spec.rs in a text editor and add genesis storage for nodes that are authorized to join the network - 

use sp_core::OpaquePeerId; // A struct wraps Vec<u8> to represent the node `PeerId`.
use node_template_runtime::NodeAuthorizationConfig; // The genesis config that serves the pallet.

Locate the testnet_genesis function that configures initial storage state, the function looks like this - 

/// Configure initial storage state for FRAME modules.
fn testnet_genesis(
 wasm_binary: &[u8],
 initial_authorities: Vec<(AuraId, GrandpaId)>,
 root_key: AccountId,
 endowed_accounts: Vec<AccountId>,
 _enable_println: bool,
 ) -> GenesisConfig {

Within the GenesisConfig, add the following code - 

 node_authorization: NodeAuthorizationConfig {
   nodes: vec![
     (
       OpaquePeerId(bs58::decode("12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2").into_vec().unwrap()),
       endowed_accounts[0].clone()
     ),
     (
       OpaquePeerId(bs58::decode("12D3KooWQYV9dGMFoRzNStwpXztXaBUjtPqi6aU76ZgUriHhKust").into_vec().unwrap()),
       endowed_accounts[1].clone()
     ),
   ],
 },

Now that we have made all the required changes to the code, we can verify if the node compiles. To compile the node, change to the root directory and compile by running the following command - 

Cargo build - -release

Setup account keys to be used

There are pre-defined keys mentioned in the substrate documentation and we will use the subkey program for setting these.

For this tutorial, you can copy the node key to a file, then use the subkey inspect-node-key to verify the peer identifiers for Charlie and Dave. For example, save the node key for Charlie to a file named charlie-node-key by using a command like the following:

echo -n "3a9d5b35b9fb4c42aafadeca046f6bf56107bd2579687f069b42646684b94d9e" > charlie-node-key
You can then run the following command to verify the peer identifier:
./subkey inspect-node-key --file charlie-node-key
The command displays the peer identifier for the Charlie node:
12D3KooWJvyP3VJYymTqG7eH4PM5rN4T2agk5cdNCfNymAqwqcvZ

Launch network nodes

Let’s start the first node by opening the terminal, going to the root of the project and running the following command - 

./target/release/node-template
--chain=local
--base-path /tmp/validator1
--alice
--node-key=c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a
--port 30333
--ws-port 9944

So we’re essentially starting it with Alice’s key and now the next node that we will start will be with Bob’s key 

./target/release/node-template
--chain=local
--base-path /tmp/validator2
--bob
--node-key=6ce3be907dbcabf20a9a5a60a712b4256a54196000a8ed4050d352bc113f8c58
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2
--port 30334
--ws-port 9945

Now let’s add a third node with charlie’s key - 

./target/release/node-template
--chain=local
--base-path /tmp/validator3
--name charlie 
--node-key=3a9d5b35b9fb4c42aafadeca046f6bf56107bd2579687f069b42646684b94d9e
--port 30335
--ws-port=9946
--offchain-worker always

We will notice that after we start the third node, there are no connected peers to this third node and that we need to provide it access since we’re running a permissioned network. The Alice and Bob nodes were configured in the genesis chain_spec.rs file. All other nodes must be added manually using a call to the Sudo pallet and that’s exactly what we’ll do.

We will now use the portal application (substrate front end) to do this - 

  • Open the Polkadot/Substrate Portal in a browser.
  • Click Developer and select Sudo.
  • Select nodeAuthorization and select addWellKnownNode(node, owner).
  • Copy and paste the hex-encoded peer identifier for the node owned by Charlie after the required 0x prefix.
  • Select Charlie as the node owner.
  • Click Submit Sudo.
  • In Authorize transaction, note that the Alice development account is the default root administrative account and used as the sudo origin for this call, then click Sign and Submit.
  • Click Network and select Explorer to view the recent transactions.

After this is done, you will note that all three nodes are now connected and are starting to sync their blocks.

Allow connections from subnode

Now we will add Dave which is a sub-node of Charlie, since this is a permissioned network, Dave sub-node can connect to the network by connecting to the Charlie node. To allow the sub-node to access the network, perform the following steps -

  • Open the Polkadot/Substrate Portal in a browser.
  • Click Developer and select Extrinsics.
  • Select nodeAuthorization and select addConnections(node, connections).
  • Copy and paste the hex-encoded peer identifier for the node owned by Charlie after the required 0x prefix.
  • For the connections parameter, copy and paste the hex-encoded peer identifier for the node owned by Dave after the required 0x prefix, then click Submit Transaction.
  • Review the transaction details, then click Sign and Submit.

Before starting the sub-node, the owner should claim the peer identifier for his node, you can do the same following the steps below - 

  • Open the Polkadot/Substrate Portal in a browser.
  • Click Developer and select Extrinsics.
  • Select nodeAuthorization and select claimNode(node).
  • Copy and paste the hex-encoded peer identifier for the node owned by Dave after the required 0x prefix, then click Submit Transaction.
  • Review the transaction details, then click Sign and Submit

Now let’s start the node with the following command - 

./target/release/node-template
--chain=local
--base-path /tmp/validator4
--name dave
--node-key=a99331ff4f0e0a0434a6263da0a5823ea3afcfffe590c9f3014e6cf620f2b19a
--port 30336
--ws-port 9947
--offchain-worker always

To allow connections to this sub-node, follow the following steps - 

  • Open the Polkadot/Substrate Portal in a browser.
  • Click Developer and select Extrinsics.
  • Select nodeAuthorization and select addConnections(node, connections).
  • Copy and paste the hex-encoded peer identifier for the node owned by Dave after the required 0x prefix.
  • For the connections parameter, copy and paste the hex-encoded peer identifier for the node owned by Charlie after the required 0x prefix, then click Submit Transaction.
  • Review the transaction details, then click Sign and Submit.


Rise In Logo

Rise together in web3