bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.6k stars 2.08k forks source link

Discover / Scan XPUB Addresses #1603

Closed Droppix closed 3 years ago

Droppix commented 3 years ago

Hi,

For several days I have been trying to find a solution but without success... I'm trying to scan a number of accounts until I find an empty address, but from an XPUB that the user could give me.

Example: m/44'/0'/0/0...m/44'/0'/0/19 (if balance != 0 -> continue scan) m/44'/1'/0/0...m/44'/0'/0/19 (if balance != 0 -> continue scan) m/44'/2'/0/0...m/44'/0'/0/2 (found address with balance 0)

or

m/49'/0'/0/0...m/44'/0'/0/19 (if balance != 0 -> continue scan) m/49'/1'/0/0...m/44'/0'/0/19 (if balance != 0 -> continue scan) m/49'/2'/0/0...m/44'/0'/0/19 (found address with balance 0)

Pseudo-code

let account = 0;
let index = 0;
let rootkey = Bitcoin.bip32.fromBase58(XPUB, network);
while (true) {
  let address;

  let key = rootkey.derive(account).derive(0).derive(index);
  let BIP = ''; //--- Detect BIP from key (I'm not sure that's possible) ----
  if (BIP == 'BIP44') {
    address = Bitcoin.payments.p2pkh({
      pubkey: key.publicKey,
      network: network,
    }).address;
  } else if (BIP == 'BIP49') {
    address = Bitcoin.payments.p2sh({
      redeem: Bitcoin.payments.p2wpkh({
        pubkey: key.publicKey,
        network: network,
      }),
      network: network,
    }).address;
  }

  let balance = readBalance(address);
  if (balance == 0) {
    console.log('Address available : ' + address);
    break;
  }
  if (index == 19) {
    account += 1;
    index = 0;
  }
  index++;
}

Thanks for you help/suggestion.

junderw commented 3 years ago
  1. BIP44 first receiving key, first account, bitcoin, is m/44'/0'/0'/0/0 you are missing a layer.
  2. m/44'/0'/0' and m/44'/0'/1' are DIFFERENT xpubs. If you want to derive multiple BIPS and/or multiple accounts, you need the master mnemonic phrase (or xprv private key) OR you need one xpub for each bip/account/coin combination.
  3. Because of this you can't derive account... so rootkey.derive(0).derive(index) would give you the receive keys (but not the change keys.)

Electrum uses xpub for BIP44, ypub for BIP49 and zpub for BIP84... but no other wallet uses this format and it is not in any BIP anywhere to use these new formats.

So if you want to get the "type" of wallet from your users, ask them.

junderw commented 3 years ago

So other than that, and the fact that you are ignoring change addresses, your code should be fine with the fixes I mentioned.

Let me know if you still can't get it to work.

Droppix commented 3 years ago

Thanks for your response.

So it's not possible from one public key calculated from the path: "m/44'/0'", derive a multi-accounts ? Or It can only be done from a mnemonic/seed/private key?

for example:

const rootKeyFromCoinType = bip32.fromBase58("xpub from m/44'/0'", network);
let key = rootKeyFromCoinType.derive(account).derive(0).derive(index)
...

So, the problem is that users in most cases don't want to provide a "mnemonic/seed/private key" for security reasons, and request multiple xpubs (for access accounts -> 20 indexes per account, that's quickly reached), it remains far too complicated for the user ...