I've been fascinated by Bitcoin for a while but did not dig into the tech until recently. I built a proof-of-concept transaction notification app in Elixir that listens for transactions from
bitcoind and sends out SMS notifications when a transaction happens that you're interested in.
(The app currently has some limitations that prevent it from being viable for use in a production context, these are listed in the issues.)
So I got
bitcoind running and talking to Elixir over ZeroMQ and RPC - this was straightforward; if you're unfamiliar with these, ZeroMQ pushes messages to you when there is new information (like it says "Hey Elixir, here's a new transaction!") and you use RPC to request information from
bitcoind (like "Hey, bitcoind, what's the best block?"). They're the two ways you can interact with
bitcoind from the outside. So far so good -- that setup was fun but hadn't surprised me yet.
The first thing that surprised me was the Bitcoin specification. I'd heard before that Bitcoin Core -- the official C++ implementation of Bitcoin -- is the specification. But I figured surely by now other people were probably publishing regularly updated distilled versions in a format that is easier to consume. As far as I know, if anybody is doing that, they're keeping it internal -- if you're working with Bitcoin you'll inevitably be reading the C++ source. There are some very helpful resources but when I followed them without checking the reference client, I saw differences when I ran my code on a Bitcoin network.
Last time I noticed, I had about 14,000 Bitcoin. Before you say 'oh dang', they were on
regtest, and they're unspendable on the main net.
Before I started this project I wondered how I'd be able to test it out without spending real Bitcoin.
bitcoind makes it easy to run three different networks:
- main net, the network where you can spend/receive real Bitcoin
- testnet, a network where you interact with other test nodes over the internet. It's easier to mine blocks, but still needs network consensus
- regtest, a local network where you can easily mine blocks on-demand
This was by far the most surprising part for me.
While I was building transaction notifications, I needed to find the source and destination Bitcoin addresses for a given transaction. You can interact with
bitcoind over the command line (it uses the RPC interface) -- and one of the commands will show you the details of a transaction. It contains an 'addresses' field, which is just a list of destination Bitcoin addresses, so I thought determining the destination addresses for a given transaction would be trivial.
It turns out that a Bitcoin transaction does not simply contain a 'destination'. Instead it embeds a scripting language. Each transaction output includes a script that must be evaluated in order to spend the coins. You can spend the coins if you are able to provide inputs to the script that cause the script evaluation to return
true. You provide these inputs when you create a new transaction. Bitcoin nodes will run the script with your input and verify that it returns
true, otherwise they'll reject the transaction and won't accept it in a block.
The scripting language has a limited number of valid operations, but a script doesn't strictly even need to use a Bitcoin address to allow somebody to spend coins. You can create a transaction that allows coins to be spent by anybody, or make a 'provably unspendable' transaction that can never be spent, and you can also just use the transaction script to carry arbitrary data. Ordinary Bitcoin clients will ignore this data but it can be used by services built on top of Bitcoin.
For the purpose of the transaction notification app I built, I discovered that I needed to parse the scripts in order to extract the destination addresses. I decided to translate the standard script parsing from the reference client into Elixir -- you can compare them here:
These routines are able to extract Bitcoin addresses from scripts that follow these standard templates. Most Bitcoin clients will be using these standard template scripts to create transactions. But it's totally possible that somebody could create a valid, spendable transaction using a custom script, and these wouldn't be able to tell you the destination.
Oh yeah, it turns out the term
coinbase isn't only a fun name for a Bitcoin service, it's also the term for the transaction in a block that generates new coins and gives them to the miner! Usually transactions need to specify both inputs and outputs. The coinbase transaction doesn't have any inputs -- it's how coins enter the money supply.
Best != Latest
This is just a thing about using precise language, but when you're interested in getting the tip of the blockchain, you get the 'best' block. I think it's easy to slip and say 'latest', but the best block is not necessarily latest; the best block is the block that belongs to the longest chain.