Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

HBitcoin: High level C# Bitcoin Wallet Library - Keep Your Coins Safe

0.00/5 (No votes)
21 Feb 2017 1  
HBitcoin is a privacy focused Bitcoin library on top of NBitcoin for .NET Core

Background

A few years ago, I attempted to write wallet library, that was my first, learning Bitcoin project, called HiddenBitcoin. While I was not able to give out a stable release, I reused and polished a lot of the code I wrote there over the years. HBitcoin can be thought of as its successor, but while my motivation for HiddenBitcoin was to learn, my motivation for HBitcoin came from a need. I kept reusing my code within different Bitcoin projects. It got to the point that the main class I'll present to you here at some point had 6 different versions. Therefore, it was time to get things right once and for all and package them into a Bitcoin library I can use any time through a simple NuGet package.

Introduction

The HBitcoin library is written on top of NBitcoin. Imagine HBitcoin as somewhere in between NBitcoin and Blockchain.info's API. It gives you more flexibility, than the latter, but less than the former. To gain a real deep knowledge on Bitcoin, you will want to check out the The C# Bitcoin Book.

By the end of this three part tutorial, you will be able to build a Bitcoin wallet.

Quote:

While programming to an API can assist in getting an application up quickly, the developer is limited to innovations that can take place against the API.

Nicolas Dorier, Bitcoin Core developer and creator of NBitcoin, the C# Bitcoin library.
Keep this quote in mind while using my library.

What Does A Bitcoin Wallet Do?

A Bitcoin wallet have three key functions:

  1. Securely stores keys and manages the access to them
  2. Monitors the transactions regarding these keys
  3. Builds transactions and broadcasts them to the network

How to Store the Keys?

Bitcoin Addresses, Private Keys

You are probably familiar with Bitcoin addresses, where you can send bitcoins and know that with the corresponding private keys, you can spend your funds.

Change Addresses

You might be also familiar with Bitcoin wallets and know that they are generating different change addresses for different transactions. You might ask why they do not use only one Bitcoin address? For privacy reasons. The Bitcoin blockchain is a public ledger, so anyone can easily see what comes in and out of an address. Therefore, it is a better idea to avoid address reuse, although that does not completely solve the privacy problem, it definitely improves it.

HD Wallets

Now we have a different problem, how can we manage so many keys? Store, monitor and spend them?
Note that the introduction of multiple keys has greatly elevated the complexity of our wallet.

Luckily, the HD wallet structure enables us to store only one key and derive the others from it. To keep ourselves consistent to NBitcoin's terminology, we'll call it `BitcoinExtKey`.

Bitcoin Improvement Proposals Regarding Key Management

There are four BIPs our concern here. BIP32, BIP38, BIP43, BIP44. For simplicity, I'd like you to think of BIP32 and BIP38 as the same BIPs. They are defining low level stuff, like how to derive and encrypt keys. These are implemented by NBitcoin. BIP43 and BIP44 are built upon BIP32-38 and define more of a structure on how the keys should be organized and used. BIP43-44 is implemented by a few wallets. I started to implement them myself, but shortly I decided against them, because they would not only greatly overcomplicate the usage of my objects, but also I was not able to incorporate in them some of the edge cases I am planning to work on in the future. This way, I can present a simpler interface.

Talk is Cheap, Guide Me Through the Code!

Project Setup

  1. Start a new .NET Core project
  2. Add the HBitcoin NuGet package
// First specify the network youll be working on
var network = Network.Main;
// Next choose a super strong password, like "password"
var password = "password";

// Create a safe at the specified path with a password on the specified network
// The Safe class helps you mange your seed properly
// The Safe will handle the serialization automatically
// By creating a Safe, it also saves itself to the specified path
Mnemonic mnemonic;
Safe safe = Safe.Create(out mnemonic, password, @"Wallets\Wallet.json", network);

// Safe creation has created a mnemonic, too, you can use it to recover (or duplicate) the safe
// You will only encounter this mnemonic at creation time
Console.WriteLine(mnemonic);

// Rrecover the safe into another file
Safe recoveredSafe = Safe.Recover(mnemonic, password, @"Wallets\SameWallet.json", network);

// Decrypt/load an existing safe/walletfile
Safe loadedSafe = Safe.Load(password, @"Wallets\hiddenWallet.hid");

// After we load a safe it is not a bad idea to check if it is on our expected network
if (network != loadedSafe.Network)
	throw new Exception("Wrong network");

// List out the first 10 addresses
for (var i = 0; i < 10; i++)
{
	Console.WriteLine(safe.GetAddress(i));
}

How Can I Hack the Wallet?

You just have to get the (password AND the mnemonic) OR (the password AND the wallet file.)

Then, you can just call Recover of Load.

Who knows the password? The user.
Who knows the mnemonic? Ideally, it is stored in the user's home written in some paper as a backup.
Who knows the wallet file? Ideally, it is stored on the user's hard drive.

Other Wallets

Usually, it is enough to recover another wallet solely with the mnemonic. I don't find that to be a good approach, therefore this wallet doesn't work like that.

CreationTime

var safe2 = Safe.Recover(mnemonic, password, "Wallet.json", network,
	creationTime: DateTimeOffset.ParseExact("2017-02-20", 
	"yyyy-MM-dd", CultureInfo.InvariantCulture));
Console.WriteLine(safe.CreationTime);

When you create a wallet, it automatically saves its creation time, too, which will come in very handy on writing some SPV wallet. Therefore, when you recover the wallet, you can also feed it with a creation time, if you don't, it'll default to the earliest creation time possible, which is hardcoded:

public static DateTimeOffset EarliestPossibleCreationTime
	=> DateTimeOffset.ParseExact("2017-02-19", 
	"yyyy-MM-dd", CultureInfo.InvariantCulture);

This is the date when I first introduced the concept of creation time. Not if you'd try to recover it with an earlier creation time, it'll use the EarliestPossibleCreationTime instead.

SafeAccounts and HdPathType

var alice = new SafeAccount(id: 2);
safe.GetAddress(index: 1, hdPathType: HdPathType.Receive, account: alice);
safe.GetPrivateKey(index: 1, hdPathType: HdPathType.Receive, account: alice);

You can create accounts if you want. In the above code, I created Alice's account with the id=2 and retrieved some keys of her.

You can also notice I specified the HdPathType as receive. This is the default HdPathType, if you don't specify anything. Note some terminology uses the words "external" and "internal" for receive and change addresses. It becomes important later when you are receiving and spending your funds of your wallet. Always receive funds to HdPathType.Receive and when you spend those funds, always receive the change back to HdPathType.Change. This adds a little more privacy to your wallet. The alternative would be when you spend a receive address you get back the change to the same address. I certainly advise against that.

The End

I would advise against implementing your own key storage scheme, except if you really know what you are doing. If for some reason my Safe is not flexible enough for your needs, open an issue on GitHub and I'll see what I can do about it.

If you liked this tutorial and would like to see more, throw me a pizza: 1LYLuYMXkCXDxSfsNoDp8FCb2mA36r29u7.

License Agreement

If you did not like my tutorial, then by reading this line, you accept to perform a Cersei Lannister walk of shame.
Moreover after your death, I will be entitled to take away your soul.

Updates

  • 2017.02.21
    • Adjust the HiddenBitcoin tutorial to its successor: HBitcoin

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here