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 moneypot 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)
pubmp1q2rvlx8p37k4np4uf8yltmr95m622a9h7m37vvtjxfc2gc4hrysxz4l9q3x, 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 moneypot 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:
Note: you don't necessarily have to derive by
purpose, we will however do so in the spirit of continuity.
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.
If you use the above fundingPublicKey together with the same claimantGenerator (which derived from the following mnemonic)
sugar subject wreck bitter comfort old robust suggest flight grant shiver theme ladder jump mixture index record dry depart nut invite cart action settle, it should generate the same address as the above example