Part 19: cyphernode II

cryptoskillz
Bitcoin e-commerce development
9 min readAug 18, 2019
Photo by True Agency on Unsplash

Introduction

This guide aims to program a website to accept Bitcoin. In the previous tutorial, we set up and configured “cyphernodewhich is an excellent dockerised bitcoin and lightning full node plus other useful. Now we are going to set it up to use specifically for our requirements. Namely as a commerce backend for websites.

Configuring cyphernode

We are going to make a number of changes to the tutorial in the previous “article” which was simply showing how to get “cyphernode” working in its default set up.

Remove Lightning

As we do not natively support it yet

Prune the blockchain

As we are using it to do as a commerce server and every sale will we do is moved to our cold storage via a hardware wallet (such as “Ledger”) or a full node (such as “casa”) which has been built to be a store of value.

Building cyphernode

Note, a number of these steps are a repetition of the previous “article” but it is worth repeating as they change (although subtly) in a significant manner.

Open a terminal client (we use Macs) if you use Windows use “putty or whatever terminal client you are comfortable with.

Step 1: Create a directory

mkdir cyphernode && cd cyphernode

Step 2: Run quick install of cyphernode

curl -fsSL https://raw.githubusercontent.com/SatoshiPortal/cyphernode/master/dist/setup.sh -o setup_cyphernode.sh && chmod +x setup_cyphernode.sh && ./setup_cyphernode.sh

Step 3: run through cyphernode set up

The first thing you will see a progress window (as shown in the screenshot below) that is basically the cyphernode set up downloading everything it requires to complete the installation.

Next, you will see is a prompt asking you for a password add one, obviously. Remember this password as it comes in handy (a lot) later.

After that you are presented with is the one below, press return as help is always good, right?

Next, it will prompt you to install extra software. We do not require either “Lightning” or “Opentimestamps client” yet so just press return to move onto the next stage.

Next, we want to select Testnet as we are testing.

Next, It asks to change user which is very good advice which we are going to ignore for the sake of simplicity. Go ahead and select N

Next, select “N”. To be frank, we are still not 100% sure what is “xPub” is and until we do and can make an informed decision about it we are going to ignore its existence.

Next, We have to give an encryption passphrase that gatekeeper will use for authentication. Go ahead and add one.

Next, select a port. 2009 is fine so just press return

Next, set up the IP’s we are going to run everything over 127.0.0.1 for now so just press return.

Next, Again press return as we are happy to route all traffic through port 80

Next, press return as we want to https to be on the default port.

Now, It is telling us it will look after the full node for us yay. Press return.

Next, we have to add an RPC username. “admin” is always good

Next, we have to add an RPC password.

Next, type “Y’ as we want to run the full node in “prune” mode

Next, we want to select 550 megabytes this is the default option so just press return.

Next, you can add a UA comment here if you want (we did not)

Next, Press return as we are presented with one option and luckily it is the option we want.

Next, you are going to be asked for the location of where to put the files just press return 4 times as the defaults are good and you will be presented with the below

Next, We do want to expose so press “Y” followed by return.

Again, we are happy for the full node to expose itself so press “Y” and return again.

Next, you want to choose from docker-compose or docker swarm. We selected docker-compose and suggest you do the same.

Next, type “Y” as it is always good to clean up after yourself.

Finally, you will see some output (like the screenshot below) that is the installation completed.

Once it is finished you will see the following

Useful things to know about cyphernode

Now we have been playing with cyphernode for a few weeks our understanding is growing and a result it makes sense to list the most common things that slowed us down in our studying of this excellent product.

zipped files

client.7z
Use your password you set at the being to unzip this and you will find the private keys for your wallets.

config.7z
Again, use your password to unzip this and you will find a config file that you can use to build more versions of cyphernode with this configuration.

wallet directory
There are 4 wallets referenced in the bitcoin.conf file and it is assumed (at this point) that are using some pattern to set the wallet names. Further experiment will show us what this pattern is.

watching01.dat
not sure what this wallet is for our hunch is it may be for watch only nodes. We will have to research this further.

xpubwatching01.dat
Not sure about this one either but we can infer from its name that it is to do with Xpub which we are also going to be looking into.

spending01.dat
This is the main wallet file that full node uses

ln01.dat
This is the wallet that the lightning node uses.

ECS

It has been a while since we have actually slung some code for ECS and Bitcoin development moves at a steady clip so it is nice to get back and update it.

Our main goals for this code update is to make ECS support the latest version of “Bitcoin Core” which at the time of writing this is 0.18.1.

As we were replacing our cobbled together backend with the infinitely superior “cyphernode” we thought it would be prudent to make it agnostic at this point so it would work with any backend

The latest branch that deals with code in this tutorial can be found “here

It is worth noting that we literately changed our entire back end here and because Bitcoin has been so awesomely programmed it took about a day and required about adding 10 lines of code. Working on e-commerce for well over a decade I know how hard this would have been using traditional e-commerce/banking/fintech architecture. We are really entertaining a golden time for payments.

Database base changes

None

Code Changes

Apart from the odd bug fix here and there (which you can see by looking at the “commit history” if you really want to) all of the refactorings we did was in the “server” component. So let us go ahead and break down the changes we made.

.env ”changes

As the concept of accounts has been “deprecated” in the latest version bitcoin core we changed the env var to just hold the wallet.

Note there were 4 wallets that cyphernode created and with some trial and error we found out the one we want is called ‘spending01.dat’ so we preconfigured it to this in the env vars below.

EMAILSMTP = 
EMAILUSERNAME =
EMAILPASSWORD =
WALLETPASSPHRASE =
WALLET = 'spending01.dat'
RPCUSERNAME =
NETWORK = 1
LIGHTNETWORK=2
RPCUSERNAME =
RPCPASSWORD =
RPCHOST = 127.0.0.1
RPCPORT = 18332
BLOCKIOKEY =
BLOCKIOSECRET =
PORT = 3030
CONFIRMATIONS=1
STRIKEAPIKEY=
STRIKEENDPOINT=

config.js” changes

We upgraded “bitcoin core” to 2.2.0 and it facilitated the name of the wallet we are using in the config parameter so we went ahead and did that. I would like an RPC call that returned the default wallet

const Client = require("bitcoin-core");
if (process.env.NETWORK == 1) {
client = new Client({
host: "127.0.0.1",
port: 18332,
wallet: process.env.WALLET,
username: process.env.RPCUSERNAME,
password: process.env.RPCPASSWORD
});
}
if (process.env.NETWORK == 2) {
client = new Client({
host: "127.0.0.1",
port: 8332,
wallet: process.env.WALLET,
username: process.env.RPCUSERNAME,
password: process.env.RPCPASSWORD
});
}

api.js ”changes

We removed the account and passphrase code. We may add the passphrase option back in the future but it will have to be switchable so it works with backends that have encrypted wallets (or not)

this.generateAddress = function generateAddress(uid, res) {
//create a new address in theaccount account :]
client.getNewAddress().then(address => {
//debug
//console.log(address);
//insert it into the database
db.run(
`INSERT INTO sessions(address,userid,net) VALUES(?,?,?)`,
[address, uid, process.env.NETWORK],
function(err) {
if (err) {
//debug
//return console.log(err.message);
//return error
res.send(JSON.stringify({ error: err.message }));
return;
}
//return the address
res.send(JSON.stringify({ address: address }));
}
);
return;
});
};

We also recorded the sweep function to use the much simpler “sendtoaddress function that was introduced in 0.16.0.

this.sweep = function sweep(address, res) {
let sqldata = [0];
let sql = `select * from ecs_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;
client.listUnspent(1, 9999999, [address]).then(result => {
//debug
console.log(result[0]);
if (result.length == 0) {
//debug
//console.log(result);
res.send(
JSON.stringify({
result: "nothing to sweep no unspent transactions"
})
);
return;
} else {
if (result[0].confirmations >= process.env.CONFIRMATIONS) {
amounttosend = result[0].amount.toFixed(8);
//debug
console.log("ams" + amounttosend);
//return;
client
.sendToAddress(coldstorageaddress, amounttosend)
.then(result => {
let sqldata = ["1", address];
let sql = `UPDATE sessions
SET swept = ?
WHERE address = ?`;
//run sql
db.run(sql, sqldata, function(err) {
if (err) {
}
//update the address in cold storage so it is not used again.
//build sql
let sqldata = ["1", coldstorageaddress];
let sql = `UPDATE ecs_coldstorageaddresses
SET used = ?
WHERE ecs_coldstorageaddress = ?`;
//run sql
db.run(sql, sqldata, function(err) {
if (err) {
}
//return status
res.send(JSON.stringify({ status: "swept" }));
return;
});
});
});
} else {
//return status
res.send(
JSON.stringify({
status: "not enough confirmations :" + result[0].confirmations
})
);
return;
}
}
});
});
};

webhook.jschanges

Again we removed the account and passphrase code.

this.checkPayment = function checkPayment(token, address, res) {
//get the unspent transactions for the address we are intrested in.
client.listUnspent(1, 9999999, [address]).then(result => {
if (result.length > 0) {
//check the confirmations (set int the env var)

//in any live enviorment.
if (result[0].confirmations >= process.env.CONFIRMATIONS) {
//valid
res.send(JSON.stringify({ status: 1 }));
} else {
//not valid
res.send(JSON.stringify({ status: 0 }));
}
} else {
res.send(JSON.stringify({ status: 0 }));
}
});
};

Conclusion

That is it, it was a very easy upgrade especially seeing as we completely replaced the backend. This is the beauty of Bitcoin it gives you control of every aspect of the flow. Imagine replacing your PCI server for you ecommerce provider over a weekend requiring the permission of no one :]

In the next article, we will host this and replace our test store with the new code.

--

--