Skip to content

Instantly share code, notes, and snippets.

@mutatrum
Created April 16, 2020 15:59
Show Gist options
  • Save mutatrum/48af22c0f63cc1cc7d24d82a9d67e5e3 to your computer and use it in GitHub Desktop.
Save mutatrum/48af22c0f63cc1cc7d24d82a9d67e5e3 to your computer and use it in GitHub Desktop.
Using bitcoinj to derive legacy, segwit and bech32 addresses from account xpub
import org.bitcoinj.core.*;
import org.bitcoinj.crypto.*;
import org.bitcoinj.params.*;
import org.bitcoinj.script.*;
import org.junit.*;
public class BitcoinJTest
{
private static NetworkParameters params = MainNetParams.get();
@Test
public void test()
{
xpub("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", AddressFormat.P2SH);
}
enum AddressFormat
{
P2PKH, P2SH, BECH32,
}
private void xpub(String xpub, AddressFormat format)
{
DeterministicKey rootKey = DeterministicKey.deserializeB58(null, xpub, params);
DeterministicHierarchy hierarchy = new DeterministicHierarchy(rootKey);
DeterministicKey external = deriveNextChild(rootKey, hierarchy);
derive(hierarchy, external, format);
DeterministicKey change = deriveNextChild(rootKey, hierarchy);
derive(hierarchy, change, format);
}
private void derive(DeterministicHierarchy hierarchy, DeterministicKey chain, AddressFormat format)
{
for (int i = 0; i < 10; i++)
{
DeterministicKey key = deriveNextChild(chain, hierarchy);
Address address = getAddress(key, format);
System.out.println(key.getPath() + ": " + address);
}
}
private DeterministicKey deriveNextChild(DeterministicKey rootKey, DeterministicHierarchy hierarchy)
{
return hierarchy.deriveNextChild(rootKey.getPath(), false, false, false);
}
private Address getAddress(ECKey key, AddressFormat format)
{
switch (format)
{
case P2PKH:
return LegacyAddress.fromPubKeyHash(params, key.getPubKeyHash());
case P2SH:
// https://groups.google.com/d/msg/bitcoinj/pHv4XyMbhZo/RLAkreuBAwAJ
Script script = ScriptBuilder.createP2WPKHOutputScript(key.getPubKeyHash());
return LegacyAddress.fromScriptHash(params, Utils.sha256hash160(script.getProgram()));
case BECH32:
return SegwitAddress.fromHash(params, key.getPubKeyHash());
default:
throw new IllegalArgumentException();
}
}
}
@mutatrum
Copy link
Author

How to make it work with zpub and derivation path: m/0'? (Electrum)

With an account extended public key It works just as xpub, with BECH32 as AddressFormat.

xpub("zpub6qPQ4bNULwrEAXXquDFMRFXYsvUHLNKEZJU5ymkAHBGBApJj7orxvLWN8LUm7o6GdMZnnT198GKkH5WNvCd5gKX5zYtZMhnzvGRkSGmEH5S", BECH32);

With a BIP32 Extended Public Key I'm not sure.

I don't know what kind of zpub Electrum uses.

@vurezo
Copy link

vurezo commented Feb 23, 2021

Electrum 4.0.9 -> Standard wallet -> "I already have a seed" -> chimney between slight bachelor physical energy antique own prize announce open actor

Wallet type: standard
Script type: p2wpkh
Keystore type: bip32
Master Public Key: zpub6nszmTrMkYZRKGdmpbLtsckj7pUZaii1kviDP443Hw1ZjrhKY5EwLoSt58ZQp2JuEAjV7gxMamURENc35QSnJFeuVd7kPhEsGMSbA2i95Af
Derivation path: m/0'

bc1q99ls3x2ndxgfmeal8vm4l6tla3g5yl55n5tvq3
bc1qvypl85m2j4npssp9yectdxz6v55wjm9vkdvuve
bc1qtn8acsae9qeu2793lpkp3hcn8w98m6vnhphavl

Could you make java code that gives those 3 addresses using the zpub & derivation path above? It should be the first 3 addresses in the chain. If so I'll reward you with $50 payable in crypto, just let me know how to contact you to send you. Thank you in advance!

@mutatrum
Copy link
Author

The code above works with that master public key:

xpub("zpub6nszmTrMkYZRKGdmpbLtsckj7pUZaii1kviDP443Hw1ZjrhKY5EwLoSt58ZQp2JuEAjV7gxMamURENc35QSnJFeuVd7kPhEsGMSbA2i95Af", BECH32);

Result:

[0H, 0, 0]: bc1q99ls3x2ndxgfmeal8vm4l6tla3g5yl55n5tvq3
[0H, 0, 1]: bc1qvypl85m2j4npssp9yectdxz6v55wjm9vkdvuve
[0H, 0, 2]: bc1qtn8acsae9qeu2793lpkp3hcn8w98m6vnhphavl
[0H, 0, 3]: bc1q6x29y39xxjcll4dwa068wqgw0anhucjdx0wsn8
[0H, 0, 4]: bc1qp6u33zlgh2ha5vre0geeahwlv3fh8jaqa2rs55
[0H, 0, 5]: bc1q5rpu5fne55uhjn6n50lcxfqdpjrg6eqssyw6wg
[0H, 0, 6]: bc1qqfw9dvallxskfec4ka8r45wdct0q5k2kea9laa
[0H, 0, 7]: bc1q5l78g55a7apaypd7307uetzylanv79vfvktcdk
[0H, 0, 8]: bc1qy5fpyd0p6hllzwkl7swqnxmha84xxempkwjlgr
[0H, 0, 9]: bc1q5sheygnnk9kdg4dqkgn228pezff6k6v9m4l622
[0H, 1, 0]: bc1qgewhyg77u3dypgfp3se9238a3g3jz5l0f8fhmu
[0H, 1, 1]: bc1qp9pjhm93cnzr3mceqs3ajss9mg0yw5e96z4dkt
[0H, 1, 2]: bc1qvkguddek6zgsx2nnhhkcuxe7l84y6gwe26jr95
[0H, 1, 3]: bc1quj7f2ulfqm9r65edy02yfwfrgx2d5llree470s
[0H, 1, 4]: bc1q3cq937f35rv0xp8sulae7vq0egx9rkxllhvfxz
[0H, 1, 5]: bc1q22qkwqvndx5zddtee3r54auzcgreadk7rq677e
[0H, 1, 6]: bc1q6lapvs6luf7jvcvw707tya86ma4tumzatsdeaz
[0H, 1, 7]: bc1q5qh997mqty36440zjennkqz7ana5sq54s0apne
[0H, 1, 8]: bc1qxz0y6wk3uc96jkf7l57t32vaxu74e8yj23nqzl
[0H, 1, 9]: bc1qc276w78uhmljaxdj2a8m4edyhv4k67a2m37lg2

Only thing I'm not sure about is if m/o'/1 are the change addresses. You should verify that as well.

@mutatrum
Copy link
Author

I'm not familiar with Electrum seeds, but the bip39 tool (https://iancoleman.io/bip39/) doesn't recognise those seed words. Apparently they use a different standard, as noted by https://walletsrecovery.org/.

@vurezo
Copy link

vurezo commented Feb 23, 2021

what's the code you used exactly to make that output? and email me vurezo@gmail.com with your BCH/BTC/crypto address

@mutatrum
Copy link
Author

The code is the gist. Just put the Electrum master public key zpub on line 14, with AddressType.BECH32. Run the test, that produces the output.

No need for payment, I wrote the code for myself, published for free.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment