Sometimes you'll want to programatically generate deposit addresses. Importantly deposit addresses can be generated completely offline. While it's unfortunately not standardized like bip32 it's a pretty straight forward process (and in-fact simpler than bip32).
So the first thing we need is the
fundingPublicKey, this is the servers public key. Our goal is to send the bitcoin in such a way that it can be spent by spent by the server (using the corresponding private key) but also in a way that the server can know who was responsible for sending the bitcoin. The
fundingPublicKey is just a constant, which is available from the hookedin-lib (if you choose to use it)
Or more standardly, in compressed format:
Next we're going to want to generate a (secp256k1) keypair to use as the basis for generating all our deposit addresses. This will function as the spiritual equivalent to an
xpub in bip32. The hookedin wallet exposes its address generator public key, so it makes it easy to generate (and check) you're generating compatible addresses.
In this example we will be using the address generator (public key)
pubhi1q0jcjekqlgfjw9t03c2zrknug22jnacrfvs87fvf2s4ug3dkv68uvdv5y3h, which in compressed format is:
Note: You don't need the private key of the
Address Generator to generate deposits addresses. But without it, you'll be unable to claim the funds deposited to it. So you probably want to make sure you have it safely backed up.
We now want to offset the
Address Generator public key by an
index. The index represents which number deposit address we're generating. The most obvious way of deriving from the
Address Generator would be using a simple ECC addition
addressGenerator + index*G. This would work perfectly fine, but it has some very undesirable properties (namely someone can use the result of the derivation to derive all your deposit addresses). So we want an operator more akin to:
addressGenerator + hash(concat(index, addressGenerator))*G.
This functionality is packaged up in the hookedin library as
.derive function on a PublicKey. (TODO: link to impl detail). This works on arbitrary sized integers (and buffers). You can even try really big numbers like:
So to derive our sample public key with the index 0 we get:
What need to convert our
derivedPublicKey into a scalar, so we can add it to
fundingPublicKey. For this we're going to use:
hmacsha256('tweak', derivedPublicKey) then multiply it by the eliptic curve generator
G and add it fundingPublicKey. With the resultant public key, we can convert it to a bitcoin (native segwit) address in the usual way (sha256 it, then rmd160 it, prepend 0 and convert to bech32). hookedin-lib provides convience functions for these operations. ECC addition is the
.tweak method on a public key, and
.toBitcoinAddress() will turn it into a bitcoin address (string). So putting everything together:
Remember you can edit the code (and run). So try increasing the index, and you'll get different addresses.