Part 11: Refactor of cold storage

cryptoskillz
Bitcoin e-commerce development
8 min readNov 1, 2018
Photo by Jeremy Goldberg on Unsplash

Introduction

This guide aims to program a website to accept Bitcoin. In the last tutorial (part 10), we deep dived address generation and went to quite a bit of theory this leads us to realise that our cold storage solution was entirely inadequate and hence why we are replacing it with (hopefully) a more robust solution.

We will be covering a lot of the concepts explained brilliantly (as always) by Andreas Antonopoulos (if you do not know his work check it out it really is quite brilliant) in the video below. Please watch this before continuing with the tutorial.

The SQL

We removed the cold storage address in the in the usersettings table as the address is going to change each time we call the method sweep from “app.js now.

CREATE TABLE "usersettings" ( 
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`userid` INTEGER
)

Lastly, we added a new table to store the addresses that we generate from “addressgeneration.js

CREATE TABLE "coldstorageaddresses" ( 
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`userid` INTEGER, `address` TEXT,
`used` INTEGER DEFAULT 0
)

The Code

This time around we created a brand new file and made a small change to sweep method in “app.js” and a new file called “addressgeneration.js”. The branch for this tutorial can be found here

Design choices

This is a command line program that generates an unlimited amount of Segwit address from a mnemonic passphrase. It has been designed to run offline and then you manually copy the address to the database.

This is very much intentional as your mnemonic passphrase is basically root access to your wallet and as a result should never ever be online. For more information on this check out the concept of Air Gapping here.

Generate a mnemonic passphrase

When it comes to generating passphrases there are a number of ways it can be done. Ideally, you should use a hardware wallet such as ledger (schilled affiliate link). However, for the purposes of this demo, we have used an online generator.

Note, in a production environment never ever use an online service to generate your passphrases as you do not know who is on the other side of the website or how they are generating the passphrases.

addressgeneration.js

//load commander
const program = require("commander");
const bitcoin = require("bitcoinjs-lib");
const bip39 = require("bip39");
const bip32 = require("bip32");
//set up block.io
var BlockIo = require("block_io");
var version = 2; // API version
var block_io = new BlockIo(
process.env.blockiokey,
process.env.blockiosecret,
version
);
const network = bitcoin.networks.testnet;
program.version("0.0.1").description("Generate Address");
//get the balance of the account.
program
.command("generate <mnemonic> <number>")
.alias("a")
.description("generate addresses")
.action((mnemonic, number) => {
//validate the menmonic
var res = bip39.validateMnemonic(mnemonic);
if (res != false) {
//generate a seed
const seed = bip39.mnemonicToSeed(mnemonic);
//set root
var root = bip32.fromSeed(seed);
//debug
//recursive generate address function
function generateAddress(root, addressIndex) {
//console.log(root);
//user derive path to get a child
//research
child = root.derivePath("m/0/" + addressIndex);
//generate a segwit address
var address = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network }),
network
}).address;
block_io.get_transactions(
{ type: "received", address: address },
function(error, data) {
//move on the index
addressIndex++;
//check for transaxtions
if (data.data.txs.length == 0) {
//output the address as it has never been used
console.log(address);
//check if we are under the number of requested addresses and call the function again if this is the case.
if (addressIndex <= number) generateAddress(root, addressIndex);
} else {
//call the function to get new address as this one had some transactions.
generateAddress(root, addressIndex);
}
}
);
}
console.log(
"Generating " + number + " of addresses starting from index 0"
);
//call the generate address function
generateAddress(root, 0);
}
});
program.parse(process.argv);

Let’s go ahead and bread that down a little shall we?

The first thing you will see that we have included a new package called commander this basically us to run the js code as a command line program

const program = require("commander");

Next, we are bringing back bitcoinjs-lib and a couple of its extension packages bip39 and bip32 respectively.

const bitcoin = require("bitcoinjs-lib");
const bip39 = require("bip39");
const bip32 = require("bip32");

Next, we are bringing back block_io so we can query an address to see if it has been used before. There are a number of ways we can tell if an address has been used before (technically there is no form addresses only unspent transactions but to make it easier to follow we will stick with this factoid) the one we have chosen is to see if it has a balance.

This is not a 100% necessary but the cleaner we keep things the better. It is worth noting that the way Bitcoin, blocks and fullnodes work it is impossible to get a balance for any address using RPC that you do not own, this is why we are using block.io.

I am not 100% sure as I have not seen their code but I am assuming that block.io have created a database of all Bitcoin transaction and indexed them to be able to provide a way to query them via their API. There are some fullnodes that offer this kind of functionality and we may look at them in the future.

Note, in a future version we will work on a way to remove this dependency by either using one the aforementioned notes with the indexes, indexing ourselves or just allowing a start paramater for where to start addressIndex so it can be run fully offline.

var BlockIo = require("block_io");
var version = 2; // API version
var block_io = new BlockIo(
process.env.blockiokey,
process.env.blockiosecret,
version
);

Next, we set the network to testnet (as usual)

const network = bitcoin.networks.testnet;

Next, we create the command that we can run from the CLI using commander. As you can see we have created a command called “generate” which takes 2 parameters “mnemonic” and “number”.

program
.command("generate <mnemonic> <number>")
.alias("a")
.description("generate addresses")
.action((mnemonic, number) => {
});

Next, we check that the mnemonic that we have passed in is valid. At this point we can trust that this function works, if however, you want to know how to validate it yourself you can read more about it here.

var res = bip39.validateMnemonic(mnemonic);if (res != false) {}

Next, we get the seed from the mnemonic

const seed = bip39.mnemonicToSeed(mnemonic);

Next, we get the root from the seed. This root is very interesting it is basically the first private/public key pair that can generate infinitely child key pairs, amazing huh? This is really complex stuff beyond the scope of this tutorial but you can read more about it here and if you prefer a video explanation I have handily added one below.

var root = bip32.fromSeed(seed);

Next, we call the generateAddress function pass in the root and set the addressIndex to 0. As you will see this is a recursive function we keep calling until we have generated the desired number of addresses.

generateAddress(root, 0);

Now, let us take a look at what is happening inside generateAddress function, The first thing we do is get an address using the AddressIndex (starting at 0 as we real coders) we passed in and store in a variable we have called child. As you can see we use the function called DerivePath to return the object at a set point in the tree.

child = root.derivePath("m/0/" + addressIndex);

Next, we want to use the child public key to generate a Segwit address. This function is a little clunky (took me 5 minutes or so to work out the actual structure of it) with having to pass the network twice and I 00% sure this is because of the way Segwit was bolted onto the p2sh function and will be refactored in time, but for now this is what we have.

var address = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network }),
network
}).address;

Lastly, we call block.io and check the balance of this address if is 0 we assume it has never been used and then increment our addressIndex counter, check it is still under the number of addresses we want and if it is we call the function again with the new(incremented) addressIndex value.

block_io.get_transactions({ type: "received", address: address }, function(
error,
data
) {
//move on the index
addressIndex++;
//check for transaxtions
if (data.data.txs.length == 0) {
//output the address as it has never been used
console.log(address);
//check if we are under the number of requested addresses and call the function again if this is the case.
if (addressIndex <= number) generateAddress(root, addressIndex);
} else {
//call the function to get new address as this one had some transactions.
generateAddress(root, addressIndex);
}
});

app.js

There has been a lot to take in in this tutorial and I do not want to deviate away from the core topic (too much) but there was a small change made to the “app.js sweep function to take account the new way we are using cold storage addresses which I will cover briefly.

The first thing we did was wrap the whole function in a SQL callback that basically gets the first available address from the coldstorageaddresses table

let sqldata = [0];
let sql = `select * from coldstorageaddresses where used = ?`;
//get a cold storage address
db.get(sql, sqldata, (err, result) => {
if (err) {
return console.error(err.message);
}
//save the address
var coldstorageaddress = result.address;
});

The last things we do is change the send to address to the coldstorageaddress (a bug from last time) and update the coldstorageaddresses table so that we do not use this address again but still have a record of it.

let sqldata = [0, coldstorageaddress];
let sql = `UPDATE coldstorageaddresses
SET used = ?
WHERE coldstorageaddress = ?`;
//run sql
db.run(sql, sqldata, function(err) {
if (err) {
}
//lock wallet
client.walletLock();
//return status
res.send(JSON.stringify({ status: "swept" }));
return;
});

As an aside, we will have to update the admin section to take account of the way we are now dealing with cord storage addresses but we can do that next time.

Usage

Ok, let us run it, shall we?

Open a terminal window and go to the directory where your server code is and type the following:

node addressgeneration.js generate "cry onion author disease page relax hundred couple rule sugar jungle response pyramid change depart" 10

This runs the function generate and passes in the mnemonic phrase and asks the program to return 10 addresses. Once it has finished running you will see output something like what is shown below

Generating 10 of addresses starting from index 02MtnQHFo21SzxVwF7prA5AHzFCAsZtdQTzL2MzryYWnYiQorfPTtEo597zNRSRZ4Us4sZx2Mv2xWEfkBtkmiptuXSvxXtpp4fdT6edqgq2MvddHEoAQt8Nt24Lb6KvbPiReMh319etPb2N7xFakZ8CbZ2pyLQ3sP8rfnCaKen2z4vrf2N6S4ynAZ3f3Tn7VRGcLogtRwQZM7k9qBpP2N3GgQLX7kQuHy61wfKSEsfeyXUU8nJ2sz32MxTUSkEpD1HgwEbVe9wzRQja5nqWNDGZrm2N4LtcDPecupp3eEDwCdASpkQ77fpzAMuum2MxGWc3DdQnhc962pwnQ7EfXJDkADiFT6Xy2N6eQvu2wi7k88RRc5G4J9U7EGdixdtnYfC

Simply take these address and put them in the into the coldstorageaddresses table.

Conclusion

Phew, that was a lot of theory but I think we have a neat little script and an elegant way to generate reusable cold storage addresses in a safe (depending on your opsec of course)

Next time we are going to add a few back-office such as sending email confirmations of sales etc.

--

--