Having now worked daily w/ BITBOX for over a month it’s become obvious which original abstractions were clumsy and which are buckling under the weight of a growing codebase. bitbox-cli v0.4.0 represents a broad refactoring that makes the abstractions cleaner, the code more solid and the functionality vastly superior. v0.4.0 represents a leap forward for BITBOX.

New classes

The BitcoinCash class has served us well. It was the starting point for us to gather abstractions and functionality while BITBOX got off the ground. But over time it became a drawer that things were just placed in. Too many different abstractions were sharing the same namespace.

It made sense to break BitcoinCash apart and at the heart of v0.4.0 are 3 new classes which separate methods meaningfully bringing more functionality to BITBOX and which make it easier for you to create great $BCH apps.

HDNode

The biggest addition to v0.4.0 is BITBOX.HDNode. This class generates BIP32 HD Nodes from seed buffers and creates extended private/public keys. It can derive hardened and non harded child HDNodes. It can also give you the public address in legacy and cash addr encoding, private key WIF, xpub and xpriv of any HDNode.

More Info

fromSeed

Create an HDNode from root seed buffer.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
BITBOX.HDNode.fromSeed(seedBuffer);

derive

Derive a non hardened child HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// derive unhardened child HDNode
BITBOX.HDNode.derive(hdNode, 0);

deriveHardened

Derive a hardened child HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// derive hardened child HDNode
BITBOX.HDNode.deriveHardened(hdNode, 0);

derivePath

Derive a child HDNode from path.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// derive BIP44 $BCH hardened child HDNode
BITBOX.HDNode.derivePath(hdNode, "m/44'/145'/0'");

toLegacyAddress

Get the legacy address of an HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// to legacy address
BITBOX.HDNode.toLegacyAddress(hdNode);
// 14apxtw2LDQmXWsS5k4JEhG93Jzjswhvma

toCashAddress

Get the cash address of an HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// to cash address
BITBOX.HDNode.toCashAddress(hdNode);
// bitcoincash:qqrz6kqw6nvhwgwrt4g7fggepvewtkr7nukkeqf4rw

toWIF

Get the private key in wallet import format (WIF) of an HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// to WIF
BITBOX.HDNode.toWIF(hdNode);
// L5E8QjFnLukp8BuF4uu9gmvvSrbafioURGdBve5tA3Eq5ptzbMCJ

toXPub

Get the extended public key (xpub) of an HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// to extended public key
BITBOX.HDNode.toXPub(hdNode);
// xpub661MyMwAqRbcG4CnhNYoK1r1TKLwQQ1UdC3LHoWFK61rsnzh7Hx35qQ9Z53ucYcE5WvA7GEDXhqqKjSY2e6Y8n7WNVLYHpXCuuX945VPuYn

toXPriv

Get the extended private key (xpriv) of an HDNode.

// create mnemonic
let mnemonic = BITBOX.Mnemonic.generate(128);
// create seed buffer from mnemonic
let seedBuffer = BITBOX.Mnemonic.toSeed(mnemonic);
// create HDNode from seed buffer
let hdNode = BITBOX.HDNode.fromSeed(seedBuffer);
// to extended private key
BITBOX.HDNode.toXPriv(hdNode);
// xprv9s21ZrQH143K2eMCcbT4qwwRhw6qZaPaEDWB792bnrxQZPoP2JUk4kfEx9eeV1uGTAWAfCqYr4wDWo52qALiukizKwQzvEyNR1fWZJi97Kv

fromXPriv

Create an HDNode from an xpriv.

// create hdNode from xpriv
BITBOX.HDNode.fromXPriv('xprv9s21ZrQH143K2b5GPP6zHz22E6LeCgQXJtwNbC3MA3Kz7Se7tveKo96EhqwFtSkYWkyenVcMqM7uq35PcUNG8cUdpsJEgwKG3dvfP7TmL3v');

fromXPub

Create an HDNode from an xpub.

// create hdNode from xpub
BITBOX.HDNode.fromXPub('xpub661MyMwAqRbcFuMLeHkSbTNwNHG9MQyrAZqV1Q4MEAsmj9MYa5sxg8WC2LKqW6EHviHVucBjWi1n38juZpDDeX3U6YrsMeACdcNSTHkM8BQ');

fromWIF

Create an ECPair from a WIF. It can generate legacy and cashaddr addresses, sign transactions and messages but not derive child HDNodes.

// mainnet WIF
let wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
BITBOX.ECPair.fromWIF(wif);

Mnemonic

BITBOX.Mnemonic generates mnemonics of 12, 15, 18, 21 and 24 words from entropy. It supports 8 languages including english, chinese traditional/simplified, japanese, korean, spanish, french and italian. It can turn the mnemonic back to entropy. It’s also able to take the mnemonic and create HD root seeds as buffers or encoded in hex.

Also included in BITBOX.Mnemonic are helper methods for validating mnemonics, finding the nearest word in a mnemonic wordlist and generating public address/private key WIF pairs.

More Info

generate

Generate BIP39 mnemonic from entropy

// generate 12 word mnemonic
BITBOX.Mnemonic.generate(128);
// boil lonely casino manage habit where total glory muffin name limit mansion

entropyToMnemonic

Create mnemonic from entropy

// generate 16 bytes of entropy
let entropy = BITBOX.Crypto.randomBytes(16);
// <Buffer 73 7b 3c 13 3f ca d1 6e 26 1e a8 79 7f e6 af 5a>
// turn entropy to 12 word mnemonic
BITBOX.Mnemonic.fromEntropy(entropy)
// security question relief cruel nephew jump chest copper axis assist gift correct

toEntropy

Turn mnemonic to entropy

// turn 12 word mnemonic to entropy
let mnemonic = 'security question relief cruel nephew jump chest copper axis assist gift correct';
BITBOX.Mnemonic.toEntropy(mnemonic)
// <Buffer c2 d5 f2 d5 1a 49 44 f1 c9 e1 7f 10 e1 b9 87 18>

validateMnemonic

Validate mnemonic

BITBOX.Mnemonic.validate('ca', BITBOX.Mnemonic.wordLists().english)
// ca is not in wordlist, did you mean cabbage?

toSeed

Create root seed buffer from mnemonic

BITBOX.Mnemonic.toSeed('enable stem left method one submit coach bid inspire cluster armed bracket')
// <Buffer 0a fa b7 46 8f 0c df 79 0f 0e 44 37 45 0c 33 c3 c8 27 17 42 75 d6 13 02 c3 55 de ef 2e 69 57 e4 f5 dd 55 b6 a8 73 78 6d b8 09 36 75 af 4f 6b 2c 52 63 ... >

wordLists

Return mnemonic word lists

BITBOX.Mnemonic.wordLists();
// {
//   EN: [],
//   JA: [],
//   chinese_simplified: [],
//   chinese_traditional: [],
//   english: [],
//   french: [],
//   italian: [],
//   japanese: [],
//   korean: [],
//   spanish: []
// }

toKeypairs

Returns an array of privateKeyWIF/publicAddress pairs. It generates the addresses as the nth external change address of the first account from that mnemonic w/ this derivation path: m/44’/145’/0’/0/n

let entropy = BITBOX.Crypto.randomBytes(32);
let mnemonic = BITBOX.Mnemonic.fromEntropy(entropy);
// symptom owner ridge follow buffalo choose stem depend million jar lemon claw color credit remove model pudding slot fiber west heavy ranch bird wet

// Then call toKeypairs and pass in your mnemonic and how many keypairs you'd like
BITBOX.Mnemonic.toKeypairs(mnemonic, 5)
// it returns the following array.
[ { privateKeyWIF: 'Kz6b1TszeUGaypUpRCnfD2L17bQSW93o4j3VMpvT5e5BqaF9XkyP',
address: 'bitcoincash:qp8a4vzfk9kstwsl4ud4ym3z2tckdf7a4gfwkxvtfq' },
{ privateKeyWIF: 'L5ZHQ2BdTQaTq2A8HNsdkHYKPLsfrHgvJyrVxHFFZyN9K3fmeoiG',
address: 'bitcoincash:qq5nxh27up6hcm0nn36lxtu7n8a7l6jsj52s8dvtex' },
{ privateKeyWIF: 'KwyY3Z7STwbxnmQXe1vVmXhT8Y3W1BJQpRgteRhTWCyvvdro2j33',
address: 'bitcoincash:qzj9n9jmnmyeqfdc5k65kxta3c7ch0g3wudeyjeg3y' },
{ privateKeyWIF: 'KxMG2mjL8DZQCaoXz8aFw5XYqguKiDHBb16JwDQMGa7ga7kfy9cE',
address: 'bitcoincash:qrhj0lesz6sn7l4hc5arh5tt8k583ahdaun6mcdjx8' },
{ privateKeyWIF: 'Kz3qqJ8GFSSbDrBqtV7mfhBoDPkSmMKtp7Yk62psDgmRjyU8id8J',
address: 'bitcoincash:qp8xjllc75c2hgrpjy3f6kegtfqgmn72dqs0y20anv' } ]

findNearestWord

Returns nearest matching word from provided word list

// english
let word = 'ab';
let wordlist = BITBOX.Mnemonic.wordLists().english;
BITBOX.Mnemonic.findNearestWord(word, wordlist);
// abandon

Tests

bitbox-cli 0.4.0 comes w/ a new and improved test suite. Each class now has it’s own test file and fixtures. We’re aiming for 100% test coverage and currently have over 1000 passing tests and are adding more daily.

To run the tests first clone the repo and then run the test suite w/ npm.

git clone https://github.com/bigearth/bitbox-cli
cd bitbox-cli
npm install
npm test
// 1014 passing (7s)

Backwards compatiblity

BITBOX v0.4.0 isn’t backwards compatible. However the main changes which need to be made to port an app from v0.3.x to v0.4.0 are to move any calls to methods which were on BITBOX.BitcoinCash that have now been moved to BITBOX.Address, BITBOX.Mnemonic or BITBOX.HDNode.

// v0.3.x
BITBOX.BitcoinCash.generateMnemonic(128);
// v0.4.x
BITBOX.Mnemonic.generate(128);

// v0.3.x
BITBOX.BitcoinCash.toLegacyAddress('bitcoincash:qpnfdl5umwl39u263kkxxnk6aa30cwrymg8v8p5j2f');
// v0.4.x
BITBOX.Address.toLegacyAddress('bitcoincash:qpnfdl5umwl39u263kkxxnk6aa30cwrymg8v8p5j2f');

// v0.3.x
BITBOX.BitcoinCash.fromSeedBuffer(seedBuffer);
// v0.4.x
BITBOX.HDNode.fromSeed(seedBuffer);