Writing bitcoin wallets in Rust is easy with
bdk, what if we want to write a wallet
for one of the altcoins that forked off from bitcoind. Well with some research
and a little bit of hacking we are off to the races.
Recently I was tasked with adding support to a software wallet for two coins
that both forked the bitcoind codebase, Dogecoin and Litecoin. We already have
support for Bitcoin by way of bdk and since bdk is super nice to use I
wanted to use it for the others also.
bdk, like most software in Rust that works with Bitcoin, uses a bunch of
repositories that all live under the
rust-bitcoin organisation on GitHub (links
here are to my forks).
- bitcoin_hashes
- rust-secp256k1
- rust-bitcoin
- rust-miniscript
- rust-bitcoincore-rpc
(Used by test code in
bdk) - I won’t discuss it further.
Turns out that to get altcoin support working I only had to hack rust-bitcoin,
rust-miniscript, and bdk.
Address prefixes
I’m no expert on Litecoin or Dogecoin, as far as I could tell the only thing
that has changed, from the perspective of a wallet developer, was the address
prefixes used by the various chains. It took me a bit of digging around to find
these but eventually I found src/chainparams.cpp and came up with this table:
| Type | BTC mainnet | BTC testnet | DOGE mainnet | DOGE testnet | LTC mainnet | LTC testnet |
|----------------+-------------+-------------+--------------+--------------+-------------+-------------|
| PUBKEY_ADDRESS | 0 - 0x00 | 111 - 0x6f | 30 - 0x1e | 113 - 0x71 | 48 - 0x30 | 111 - 0x6f |
| SCRIPT_ADDRESS | 5 - 0x05 | 196 - 0xc4 | 22 - 0x16 | 196 - 0xc4 | 0x32 (0x05) | 58 - 0x3a |
| SECRET_KEY | 128 - 0x80 | 239 - 0xef | 158 - 0x9e | 241 - 0xf1 | | |
| EXT_PUBLIC_KEY | 0488B21E | 043587CF | 02FACAFD | 043587CF | | |
| EXT_SECRET_KEY | 0488ADE4 | 04358394 | 02FAC398 | 04358394 | | |
Its not complete, but that was enough to get the functionality I needed. Now herein lies an insidious problem. The astute among you might have realised that there are duplicate values in the table above, more on this below.
In order to work with addresses in rust-bitcoin we need to be able to go to
and from strings.
The Address struct currently looks like this:
/// A Bitcoin address
pub struct Address {
/// The type of the address
pub payload: Payload,
/// The network on which this address is usable
pub network: Network,
}
At first I just wanted to throw a blockchain field in there and then use that
to map between prefixes, but since I’m not as clever as I wish I was I didn’t
notice the duplicate value problem until after I’d written that code. So, once
I’d run into that wall, I added a prefix field instead.
...
/// Any prefix data we need to be able to serialize the address.
pub prefix: Prefix,
}
/// Prefix data required to serialize an address.
// This is tightly coupled with the Network, if someone mutates an address by
// changing the network without updating the prefix bad things will happen.
pub enum Prefix {
/// Pubkey hash prefix byte.
Pubkey(u8),
/// Script hash prefix byte.
Script(u8),
/// Segwit prefix characters e.g., "bc"
Segwit(String),
}
From the code comment on Prefix we can already see that this is not going to
be pretty.
For the record, this work has zero chance of upstreaming, and I would not want to push it up anyways. I’m thankful that I was able to use these libraries to get the job done but I in no way condone adding altcoin support to the Bitcoin stack.
Now we can add the Blockchain struct
/// Supported blockchains.
pub enum Blockchain {
/// The Bitcoin blockchain.
Bitcoin,
/// The Dogecoin blockchain.
Dogecoin,
/// The Litecoin blockchain.
Litecoin,
}
As we can see above an Address has two parts, the Payload and the Network.
We can work out the prefix as long as we have both of these and the chain we are
on. To stringify an Address we need the prefix.
Now to go the other way, from an address string to an Address we just parse
the prefix.
Walah, that’s more or less all there is to it. Now its just a matter of updating
the whole stack to use the new Address type and constructors. None of the
other repositories required any interesting or difficult work, you can check
them out by fetching the altcoin-support branch of each repo on my GitHub
account (linked above).
I hope this is useful if you find yourself supporting Bitcoin clones in wallet software written in Rust. Feel free to open PRs adding support for additional coins.
Thanks!