Bitcoin addresses: Crash Course for Developers

Bitcoin addresses: Crash Course for Developers14 min read

It’s another journey we are going to take tonight. Let me introduce you to Bitcoin. In this introduction journey, we will put the focus on getting you ready for work with Bitcoin addresses.

What is Bitcoin ?

I will spare you the lengthy description of what Bitcoin can do, and be, and what it is not or will never be. Instead, following is a description from the initial research paper related to Bitcoin, as published by Satoshi Nakamoto:

What is needed is an electronic payment system based on cryptographic proof instead of trust, allowing any two willing parties to transact directly with each other without the need for a trusted third party.

Satoshi Nakamoto, January 2009

In other words, Bitcoin aims to be a software system enabling people to conduct peer-to-peer online transactions without relying payment companies or banks.

In this article, we are going to have a look at the out-of-the-box financial possibilities of Bitcoin. Follow this link if you wish to see the full source code for this article now already.

Defining our toolset

As our main dependency, we will be using the bitcoin-js Javascript library. As a first, let us create a new working directory and install the library into it:

$ mkdir bitcoin-sandbox && cd bitcoin-sandbox
$ npm init -y && npm install bitcoinjs-lib

We are now already all set with our sandbox. It includes all features needed to use the Bitcoin protocol. This includes sending Bitcoin from one account to another, attaching messages to transactions or even requesting peer informations from the Bitcoin network.

Bitcoin & Public Key Cryptography

Public key cryptography is one of the systems used widely with the Bitcoin protocol. In fact, with Bitcoin we are working with addresses. Those represent account balances. But addresses are in fact just public keys in an asymmetric cryptography system, where the private key is the password. The password, which unlocks access to any Bitcoin balance stored on said address.

With this knowledge, we will now, programmatically create a first public key. This public key will then be formatted into an address. More details on this afterwards. First, create a file named main.js, like this:

$ touch main.js

Open the file and add the following snippet into it.

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

// change this "secret"..
const secret = Buffer.from('abcdefghijklmnopqrstuvwxyz012345');
const keyPair = bitcoin.ECPair.makeRandom({ rng: () => { return secret; } }); // (1)

// read the keypair and format to address
const pubKey  = keyPair.publicKey.toString();
const privKey = keyPair.privateKey.toString();
const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) // (2)

// print created address
console.log("The address is:     ", address);
console.log("The public key is:  ", pubKey);
console.log("The private key is: ", privKey);

In point one (1), we create a [not-so-secret] secret, from which we then create an elliptic curve keypair. This keypair is the key to our Bitcoin address. As you can see above the point two (2), the keyPair variable is used to read the public key and the private key. Later, the public key is used to create a Bitcoin address. We’ll discuss address generation in detail a little bit later.

Executing this example with a simple node main.js in the terminal, will give you this output:

Bitcoin address generation with bitcoinjs-lib

So we’ve got our first address: 1FUCPzYpB7WcAamF25pk8sV1qd93ivw4nz. Obviously it is not recommended to generate private keys directly out of a passphrase like it is done in this example.

To be safe, and for the sake of your first sandbox, you can use passwords with 128 characters, mixing special characters, numbers and alpha numerical characters all together.

Now that you know how to generate a new address, go ahead and do so. Next, we will create a different type of wallet. As you may know: in order to spent the funds available on the address we created in the first example, we would need the one private key.

This configuration is sometimes not secure enough. With Bitcoin, it is possible to secure your wallets with multiple keys in so-called multi-signature addresses.

Multi-Signature Addresses

With Bitcoin multisig addresses, it is possible to pre-define multiple actors, for operating one said address. This implies that these pre-defined actors are needed whenever Bitcoin is spent from this address.

In our example, we will be using a 2-of-3 multi-signature address. This address needs at least 2 co-signatures for any transaction it initiates.

Let me explain this by an example, and let us create our first multi-signature address, with the following snippet:

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

// list co-signatories
const pubkeys = [
 '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01',
 '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9',
 '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9'
].map((hex) => Buffer.from(hex, 'hex'));

// create the multi-signature address
const { address } = bitcoin.payments.p2sh({
  redeem: bitcoin.payments.p2ms({ m: 2, pubkeys })
});

// print created address
console.log("The address is: ", address);

After executing this code above, you will see the following result: 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7. This is the address that is generated deterministically when creating a 2-of-3 multi-signature schemed address with the above listed public keys. Obviously, each of these public keys is paired to a private key, which is used when redeeming the Bitcoin.

The example is not over. In order for you to understand multi-signature addresses correctly, it is important that we try to spend the Bitcoin stored on that address.

Because, in order for Bitcoin to be spent from the address 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7, we will need more than one private key. In fact, we need 2 of the 3 private keys respectively linked to the public keys from our example.

Signing with multiple keys can also mean that more than one person needs to verify the action before it gets executed. Opening the doors to automated contracts in Bitcoin addresses. More on this in the next section.

Building blocks: different address schemes

The innovating idea about Bitcoin addresses is that they do not simply represent public keys. They are also used to define contracts in Bitcoin scripts. These contracts may be used to perform any type of financial contract, without the need of a trusted third party.

As such, it is not only one third party who will define whether spending Bitcoin from one said address is allowed or not. It is the entire network of people running the software that will do so.

Now, in order to get a clearer understanding of what exactly Bitcoin Script looks like, let us inspect the two different address schemes we have seen until now:

  • P2PKH: 1FUCPzYpB7WcAamF25pk8sV1qd93ivw4nz
  • P2SH: 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7

In Bitcoin, the first type of addresses that was used was Pay to PubKey Hash (p2pkh). Take the following definition:

When you create a Bitcoin transaction to spend funds from a p2pkh address, you must pass a signature that is created with the right private key, as well as provide the public key which when it is hashed, should produce the same result as the pubKeyHash stored in the Bitcoin input script.

That is still very cryptic, isn’t it? Fine, let’s see how this Bitcoin input script actually looks like:

OP_DUP 
OP_HASH160 
404371705fa9bd789a2fcd52d2c580b65d35549d 
OP_EQUALVERIFY 
OP_CHECKSIG

For clarity, we have put each operation on its own line. The Bitcoin Script language is stack based meaning that each of those operations listed above, will be executed one after the other. To make this more understandable, let us go through the basics of Bitcoin Script:

  • OP_DUP: Duplicates the top stack item.
  • OP_HASH160: The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
  • OP_EQUALVERIFY: First run OP_EQUAL which returns 1 if the inputs are exactly equal, 0 otherwise. Then run OP_VERIFY which marks transaction as invalid if the top stack value is not 1.
  • OP_CHECKSIG: The entire transaction’s outputs, inputs, and script are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key.

These are the operations descriptions from the Bitcoin Wiki. We are now going to read the details of our Bitcoin Script with this knowledge.

OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549d

These operations can be read from left to right and are attached to one input (or one address). What they tell us is that we must hash the public key with sha-256 and then with RIPEMD-160 algorithms. This is from the OP_HASH160 operations.

Following this, the input requires a OP_EQUALVERIFY which will effectively mark the transaction as invalid in case the hashed public key from above does not match exactly the public key hash in the input, which for our example is: 404371705fa9bd789a2fcd52d2c580b65d35549d.

Of course, everyone can see this Script. But only one person should be able to provide the matching signature which can only be created with the correct private key associated to the listed public key.

Bitcoin Script is what makes Bitcoin so interesting for automated financial contracts. Let us now inspect a deeper example of Bitcoin Script and write one that is needed to secure our funds in a 2-of-3 multi-signature address like in our earlier example.

Bitcoin Script: Multi-Signature

In order to require more than one private key as to allow Bitcoin to be spent from one address, we will create a Bitcoin Script including different operations than earlier.

Following is the format of a Bitcoin Script for a 2-of-3 multi-signature address:

<OP_2> <A pubkey> <B pubkey> <C pubkey> <OP_3> <OP_CHECKMULTISIG>

From the earlier example, the script that will be created for the multi-signature address 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7 is then as follows:

OP_2
026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01
02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9
03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9
OP_3
OP_CHECKMULTISIG

Now, what this Bitcoin Script tells us is a little different from the earlier script. This time, there are also different operations taking place. Let us inspect those one by one:

  • OP_2: The number 2 is pushed onto the stack.
  • OP_3: The number 3 is pushed onto the stack.
  • OP_CHECKMULTISIG: Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match.

Now that we have this, reading our Bitcoin script is possible from left to right like what we did earlier:

OP_2 026477 02c96d 03c610 OP_3 OP_CHECKMULTISIG

Note that for the sake of clarity, public keys have been reduced in character size. What this Bitcoin Script tells us, is that 2 of the 3 available public keys must be matched to spend the funds available.

When we will try to spend the funds available on that multi-signature address, we will always have to provide two signatures for the transaction. Each of those signatures will be created with the private key associated to one of the three public keys listed above. Of course, the two signatures must be from different co-signatories each.

This is it already, for the perspective of automated co-signatories contracts with Bitcoin multi-signature addresses.

But are there really only complicated opcodes available? Of course not, lets try with a very simple Bitcoin Script:

OP_2 OP_2 OP_NUMEQUAL OP_VERIFY

Simply put, these coins would be spendable by anyone. In fact, this input doesn’t require any type of authentication in the form of a public key hash or a multi-signature list of public keys. It will always push TRUE to the stack whenever the input is included in a transaction.

If you store Bitcoin on an address with this redeem script, someone else would only need to include the input in his transaction and the Bitcoin could be spend. Without providing the right private key.

Obviously, this example is just for the sake of demonstrating other possibilities with Bitcoin Script. There is way more, and we will be inspecting some of those in our follow-up articles, stay tuned!

To conclude with this article, we will be spending Bitcoin from our 2-of-3 multi-signature address. Effectively executing our immutable financial contract.

Executing our Financial Contract

For this article to be complete, we must give you the knowledge of how to actually spend Bitcoin which you locked on a 2-of-3 multi-signature address.

This is not going to be as complicated as it sounds. In fact, all that is needed, is for us to gather the two private key corresponding to two of our three listed co-signatories. As a refresher, following is the list of public keys:

026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01
02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9
03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9

In order to spend funds locked in the address 36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7, we must provide two private keys which match the public keys listed above.

The following example creates a 2-of-3 multi-signature address with random keypairs (3 of them):

// create random keypairs
const keyPairs = [
  bitcoin.ECPair.makeRandom({ network: regtest }),
  bitcoin.ECPair.makeRandom({ network: regtest }),
  bitcoin.ECPair.makeRandom({ network: regtest })
];

// configure multi-signature address
const pubkeys = keyPairs.map(x => x.publicKey);
const p2sh = bitcoin.payments.p2sh({ 
  redeem: bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }), 
  network: regtest 
});

Now we will be creating a transaction and sign this transaction with two of the three created keypairs. As illustrated in the following snippet:

// Now building transaction
// This is where we specify the CORRECT INPUT that will be spent
const transaction = new bitcoin.TransactionBuilder(regtest);
const pubKey = Buffer.from("12345678901234567890123456789012");
transaction.addInput(pubKey, 1); // txId, vout

// CAREFUL HERE: the coins are sent to a random address!
transaction.addOutput(randomAddress(), 1e4);

// MAGIC HERE: co-signature from MULTIPLE signers
transaction.sign(0, keyPairs[0], p2sh.redeem.output);
transaction.sign(0, keyPairs[2], p2sh.redeem.output);

In the line #2 of this snippet, you can see that we are using an invalid transaction id. Instead of the pubKey that is used in the transaction.addInput(pubKey, 1) you should actually refer to a valid Bitcoin transaction id. The second parameter to that function specifies the index of the used input, in the outputs of the mentioned transaction id.

Now all that is needed is for us to build the transaction into a serialised transaction payload and then broadcast this payload to a Bitcoin node. This is the simpler part of the work, and can be achieved with the following:

// create the serialized transaction payload
const tx = transaction.build();

try {
  // broadcast the transaction
  let req = http.request({
    host: API_HOST,
    path: "/t/push",
    port: 80,
    method: "POST",
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(tx.toHex())
    }
  }, (res) => {
    console.log("Transaction broadcast!");
  });

  req.write(tx.toHex());
  req.end();
}
catch (e) {
  console.error("An error occured: ", e);
}

Right, so there we go. You are now also read to spend Bitcoin from your multi-signature addresses.

Thank you for reading!

Thank you for reading us and don’t hesitate to comment our articles and give us feedback about article topics or interesting new topics. We are always open for interesting subject ideas, please contribute to these innovating times we are living in!


We sincerely hope you enjoyed this journey across the Bitcoin technology borderlines. We will be posting more interesting insights about this technology in the weeks to come – Stay tuned!

References & Links

eVias

Founder of eVias Services, Greg provides quality development supported by a team of technology experts and developers. eVias Services stands for high quality and innovative development skills -- Feel free to browse through Greg's projects and don’t hesitate to contact him for any questions: https://github.com/evias

Leave a Reply