EasyBitcoin example: Creating a command-line wallet.(2/2)
Hi! On the last post we spoke about doing a command-line
bitcoin wallet using Haskell and the EasyBitcoin
library; we have already introduced the protocol, so now it’s time to get our
hand dirty and start coding!
Creating our own (simplified) wallet:
Our wallet will be rather simple, we’ll rely on a 3º party provider to deal with the p2p network communication, so we won’t need to install any additional software :) . Most of the time, applications communicate with the bitcoin network through a bitcoin-client hosted by themselves, this way they don’t need to rely anyone. In any case, from the code point of view, there’s no much different between calling our own client or a 3º party: it is just about adapting to their exposed API.
So let’s start importing the libraries, types and functions will need!
Of course that would not be enough, we’ll also need to read from command line, communicate with the 3º party using HTTP request and we’ll need to parse and compose those requests.
And some common helpers:
We’ll use a constant secret private key from which our receiving address will be derivate, and that we use to sign our outgoing transaction.
Nowadays, most wallets generate a new address for each incoming transaction, this way we can track who has paid us and also it helps enhancing the user’s financial privacy. Usually it has a random secret root, and from this root they derivate new key pairs for each new address, following some deterministic derivation schema like the BIP0032.
Though EasyBitcoin
has special support for BIP0032, we’ll keep things simple and use only one key and address.
Implementing main procedures:
So what should a wallet do? At least it should be able receive funds, check its current balance, and send funds to any valid address; Let’s write down these usage cases:
So now, we only need to implement print_address
, check_balance
and send_bitcoins
and we’ll be done. The first
of the 3 procedure looks rather difficult
Yes, it was even worse than expected!!
To compute our balance (check_balance
), we’ll ask the 3º party about the
UTXO pointing to our address, and we’ll sum them. We’ll distinguish
those confirmed (with 1 or more confirmations) and those funds yet to be confirmed.
Now, in order to implement send_bitcoins
, we need to compose a transaction, and send it to the 3º party so it can broadcast it into
the p2p bitcoin network. For a transaction to be valid, it needs to reference the same or more bitcoin from UTXO, than the one it is sending
(otherwise we could send more btc than what we have). But before we should check:
-
We actually have enough bitcoins to send.
-
The amount to send is not to small… transactions with tiny outputs are considered spam and rejected by the network (If doesn’t matter if we are sending 1€ million and paying $1 million on commission, currently, it is just have a single output too small, it will be considered spam).
-
The difference between the btc’s from the UTXO’s and the one sending will be lost as a fee, if this amount is too big, we can solve this problem adding another output back to ourself sending some of the remaining btc’s.
-
But if the difference is not big enough, doing that would lead us to the problem we were trying to solve on point 2.
We have forget to define some important parameters, ¿How much fee to pay?, ¿Where should be that threshold? ¿How do we select the UTXOs to use among all the available ones? For the first and second one we can check and hardcore the recommended values (they change as the network’s load and bitcoin price evolves); But the second one is a bit more complicated: if we want to select the best combination of UTXOs such it minimize the remaining returned funds (that will need around 10 extra minutes to get back confirmed), we’ll face the Knapsack problem which is NP-complete in the general case.
So as simple criteria to select a UTXO, we’ll use:
-
We’ll prefer confirmed over no confirmed.
-
If equally confirmed, we’ll arbitrarily prefer those with biggest Txid. (As Txid are derived from hashes, this would be close to pick one randomly).
Implementing the requests:
As we mentioned, we used 3º party, in our case, a block explorer, particularly blockr.io, but any one would work, as they are most of them quiet similar. Actually, it would only take a few changes to adapt it so it can use our own bitcoind client.
Time to play:
So we have finally finished and our implementation is ready to be used! I already sent some tBTC so you can already start making some transactions (unless someone has done them before you).
If you want to create your own version, don’t forget to use a different secret
(as It is posted on a blog I guess
is not really a “secret”). The EasyBitcoin
library accept both WIF and BIP0032 as format for its Read
instance.
If you want to use the official bitcoin’d’ client, just adapt procedure send
and readBlockExplorer
to use
its API.
You can find the EasyBitcoin
documentation on hackage; or just fire me a comment and I’ll try my best
to help :)