Starting with Polkadot Development (Part II)

0
21

In the previous part of this tutorial, we learned how to set up a development node in our local environment using Docker. We also launched the Polkadot.js open source UI project and connected with this node, allowing us to do transactions without broadcasting them to the entire network. Now we are going to learn by reading the code.

Creating our Project and WS Connection

Starting our node in development mode gave us a few accounts filled with DOTs (Polkadot’s currency). We could (and will) be using those, but in general, creating an account is paramount to any development effort, so we better learn how it’s done. Our task is to read through the code from the Polkadot.js project and implement it on Node.js. But since the codebase of this project is highly optimized and not as readable, we will first jump right in a working example using Node. Go into a new folder and:

npm init --yes # easy way to create Node.js project

This will create a package.json file without asking us too many questions. By default that is expecting an index.js file as an entry point to the application, so let’s add it to our project.

touch index.js

For now, we are going to run it with simply through

node index.js

Then we need to install the Polkadot module through which we are going to communicate with the network.

npm i @polkadot/api --save

Within our index.js file, we can now initialize the client in two modes, HTTP and WS (Web Sockets). Remember how in the first part of this tutorial, we started our server w --ws-external? This was meant to enable WS connection, which we are not going to use.

// index.js
const { ApiPromise, WsProvider } = require('@polkadot/api');

const connect = async () => {
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
const api = new ApiPromise({ provider: wsProvider });
return api.isReady;
};

connect().then((api) => {
console.log(`Our client is connected: ${api.isConnected}`);
}).catch((err) => {
console.error(err)
}).finally(() => process.exit());

We initialize the client via a WSProvide, the alternative being an HttpProvider. api.isReady returns a promise which resolves in the client instance, which we can then use to check the connection above is true.

Creating an Account

Next, we need to create an account like so. First, we initialize a Keyring, which you can think of as an internal store that saves a key-pair that makes up an account. More specifically, and although we are simplifying greatly, an account is made up of a public and private key. An address such as the ones generated for free upon initialization through the --aliceflag, is essentially a hash of the public key, while the private key is what signs the transactions and must be kept a secret. The Keyring is a nice way to keep the private key from being exposed to the rest of the code while maintaining the capability of signing a transaction. Add this to our file

const { mnemonicGenerate } = require('@polkadot/util-crypto');
const { Keyring } = require('@polkadot/keyring');
const keyring = new Keyring({type: 'sr25519'});
const mnemonic = mnemonicGenerate();
const account = keyring.addFromMnemonic(mnemonic);
console.log(`Address: ${account.address}`);
console.log(`Mnemonic: "${mnemonic}"`);

A private/public key-pair is usually created via an algorithm that takes a single input value called a seed. The insight here is that the algorithm is deterministic, which means that without input it would generate the same keys every time it ran, which is not what we expect now, is it? The seed differentiated the initial variable of this algorithm and so it’s usually random. That is what mnemonicGenerate() does, it creates a random seed. The value it returns however is not a number but a list of words. This method was first proposed by Bitcoin and later incorporated by most crypto-currencies. The words that you see are essentially an offset in a dictionary. Concatenate the offsets and you get the seed. As the name implies, it’s meant as an easier way to store the initialization secrete, even in your own head!

Why save it you may ask. Remember how the key-derivation algorithm was described as deterministic. That means that if you generate a key-pair using the same mnemonic, you get the same key, which is indispensable. Let’s include this functionality as well.

const { mnemonicValidate } = require('@polkadot/util-crypto');
const createAccount = (mnemonic) => {
mnemonic = mnemonic && mnemonicValidate(mnemonic)
? mnemonic
: mnemonicGenerate();
const account = keyring.addFromMnemonic(mnemonic);
return { account, mnemonic };
}

Here, we can optionally pass an existing mnemonic, check it’s valid, and use this to create the account.

const { account: acc1, mnemonic } = createAccount();
console.log(`Mnemonic: "${mnemonic}"`);
console.log(`Address 1: ${acc1.address}`);
const { account: acc2 } = createAccount(mnemonic);
console.log(`Address 2: ${acc2.address}`);

You should see the same address.

Now, you can refresh our Polkadot.js UI that we set up in the 1rst part of this tutorial all you want and you will not see this new address. So where is it? Do we need to broadcast it to the network? In short, the answer is no. An address does not really exist apart from a reference in transactions. As long as there is some transaction that includes the address, it’s visible to the network and can be queried with various tools, otherwise, it might as well not have existed.

Let’s do just that. Copy the mnemonic phrase that you see printed on your terminal and go to the Polkadot UI that is running locally

In the mnemonic section, paste the phrase that was printed on your console, check “I have saved my mnemonic…” (which is actually true), click Next, and add a password. You should see the address that you created on your Node.js application at the bottom of that list. This still does not make the address somehow “public” on the network, but we can move funds into it from one of the other accounts that were created for us when we started this Polkadot node locally.

Find the account on the drop-down list, set an amount, and click ok.

The complete code for the Node.js application looks like this:

// index.js
const { 
mnemonicGenerate,
mnemonicValidate
} = require('@polkadot/util-crypto');
const { ApiPromise, WsProvider } = require('@polkadot/api');
const { Keyring } = require('@polkadot/keyring');

const connect = async () => {
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
const api = new ApiPromise({ provider: wsProvider });
return api.isReady;
};

const keyring = new Keyring({type: 'sr25519'});

const createAccount = (mnemonic) => {
mnemonic = mnemonic && mnemonicValidate(mnemonic)
? mnemonic
: mnemonicGenerate();
const account = keyring.addFromMnemonic(mnemonic);
return { account, mnemonic };
}

connect().then((api) => {
console.log(`Our client is connected: ${api.isConnected}`);
const { account: acc1, mnemonic } = createAccount();
console.log(`Mnemonic: "${mnemonic}"`);
console.log(`Address 1: ${acc1.address}`);
const { account: acc2 } = createAccount(mnemonic);
console.log(`Address 2: ${acc2.address}`);
}).catch((err) => {
console.error(err)
}).finally(() => process.exit());

Starting with Polkadot Development (Part II) was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.