bitcoinjs-lib icon indicating copy to clipboard operation
bitcoinjs-lib copied to clipboard

React Native address derivation is slow

Open olexh opened this issue 4 years ago • 6 comments

Hello. I'm trying to generate addresses by derivation path using the code below in my React Native app, it works fine on the IOS simulator, but when I want to run the app on my device it works very slow comparing with the simulator, like 10x faster than a physical device. Anyone knows why performance so different between them and maybe it can be fixed somehow? I'm using the 5.2.0 version of the library and the react-native version is 0.63.3. Thank you in advance!

const seed = bip39.mnemonicToSeedSync("kangaroo foam chapter physical swim scheme exercise put just faint venue impulse");
const root = bitcoin.bip32.fromSeed(seed);
const branch = root.deriveHardened(0).derive(0).derive(0);
let arr = [];

for (let i = 0; i < 20; ++i) {
	arr.push(getAddress(branch.derive(i)));
}

#1057

olexh avatar Nov 06 '20 12:11 olexh

This sounds like an issue better brought up with react-native devs... "Why is it slow (in general)" might be our problem... but "Why is it 10x slower on real iOS vs. iOS emulator?" is almost definitely a react-native issue.

Just FYI, we use C++ bindings for the nodeJS environment and native JS (much slower) when bundling for browser, so maybe that has something to do with it. (I don't know how RN or the emulator works) OR it could just be you have an slow iPhone and a fast MacBook?

junderw avatar Nov 06 '20 15:11 junderw

This sounds like an issue better brought up with react-native devs... "Why is it slow (in general)" might be our problem... but "Why is it 10x slower on real iOS vs. iOS emulator?" is almost definitely a react-native issue.

Just FYI, we use C++ bindings for the nodeJS environment and native JS (much slower) when bundling for browser, so maybe that has something to do with it. (I don't know how RN or the emulator works) OR it could just be you have an slow iPhone and a fast MacBook?

Thank you for your reply. You are right about react native, I just wanted to make sure about that, I thought it could be fixed on javascript side. I should try moving bip32 and bip39 processes to native code for each platform, seems like it will work much better.

olexh avatar Nov 06 '20 16:11 olexh

I had the same issue in the past, but I was able to fix it by adding a sleep between each iteration.

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function getGeneratedAddressesAsync(
  pubKey: string,
  path: string,
  segwit: boolean,
  iteration: number,
  network: bitcoin.Network = bitcoin.networks.bitcoin
): Promise<string[]> {
  let node: bip32.BIP32Interface;
  try {
    node = bip32.fromBase58(pubKey, network);
  } catch (err) {
    console.log(err);
  }
  let derivedNode = node.derivePath(path.substring(0, path.length - 1));
  let addr: string[] = [];
  return new Promise(async (resolve) => {
    for (let i = 0; i < iteration; i++) {
      let gen: string = "";
      let btcNodeDerivation = derivedNode.derive(i);
      if (segwit) {
        let p2wpkh = bitcoin.payments.p2wpkh({
          pubkey: btcNodeDerivation.publicKey,
          network
        });
        gen = bitcoin.payments.p2sh({
          redeem: p2wpkh,
          network
        }).address;
      } else {
        gen = bitcoin.payments.p2pkh({
          pubkey: btcNodeDerivation.publicKey,
          network
        }).address;
      }
      addr.push(gen);
      await sleep(1); // delaying is mandatory, otherwise it's blocking other processes...
    }
    for (let b of addr) {
      console.log(`addr : ${b}`);
    }
    resolve(addr);
  });
}

basically, adding the sleep(1) solve the issue on my side

aaska avatar Dec 26 '20 10:12 aaska

@olexh, I know this thread is a few months old at this point, but switching to react-native-v8 yielded a significant boost in performance for my RN projects on Android. Have you managed to find any other solution?

coreyphillips avatar Dec 30 '20 16:12 coreyphillips

Try the solution with the promise timeout. It works well

vesparny avatar Dec 30 '20 17:12 vesparny

I stumbled upon the same issue a while ago, and the way I solved it:

  • cache and reuse derive() calls whenever possible
  • don't generate too many addresses upfront. you probably need only gap_limit addresses for external path and only one for internal
  • generate and cache addresses when user is not expected to interact with the app - for example, when user is supposed to stare at mnemonic seed and write it down on paper

Overtorment avatar Jan 05 '21 20:01 Overtorment