diff --git a/README.md b/README.md index ebdc30b..01b6000 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,14 @@ Generate all the keys needed for use with Stacks 2.0 Mining and Stacking ## prerequisites -You will need to have node.js and npm installed first. Head over to the [node.js download](https://nodejs.org/en/download/) page +You will need to have node.js and npm installed first. Head over to the +[node.js download](https://nodejs.org/en/download/) page. +Tested ok with `node16` but not `node18`. + +## install deps +``` +yarn install # avoid `npm install` +``` ## usage with npx If `npx` is not installed, install it first diff --git a/src/index.js b/src/index.js index 57e7a7c..4789d13 100644 --- a/src/index.js +++ b/src/index.js @@ -10,17 +10,11 @@ const stacks_transactions = require('@stacks/transactions') const c32c = require('c32check') const wif = require('wif') const yargs = require('yargs') - + const { generateMnemonic, mnemonicToSeed } = bip39 const { bip32, networks, ECPair } = bitcoin const { ChainID, getAddressFromPrivateKey, TransactionVersion } = stacks_transactions -const networkDerivationPath = `m/44'/5757'/0'/0/0` -const derivationPaths = { - [ChainID.Mainnet]: networkDerivationPath, - [ChainID.Testnet]: networkDerivationPath, -} - function privateKeyToWIF(private_key_hex, mainnet) { return wif.encode(mainnet ? 0x80 : 0xEF, Buffer.from(private_key_hex, 'hex'), true) } @@ -35,9 +29,9 @@ function ecPairToHexString(secretKey) { } } -function deriveStxAddressChain(chain) { +function deriveStxAddressChain(chain, derivationPath) { return (rootNode) => { - const childKey = rootNode.derivePath(derivationPaths[chain]) + const childKey = rootNode.derivePath(derivationPath) if (!childKey.privateKey) { throw new Error('Unable to derive private key from `rootNode`, bip32 master keychain') } @@ -70,10 +64,10 @@ function hash160(data) { return ripemd160(sha256(data)) } -async function generateKeys(seed_phrase, mainnet) { +async function generateKeys(seed_phrase, mainnet, derivationPath) { const seedBuffer = await mnemonicToSeed(seed_phrase) const masterKeychain = bip32.fromSeed(seedBuffer) - const keys = deriveStxAddressChain(mainnet ? ChainID.Mainnet : ChainID.Testnet)(masterKeychain) + const keys = deriveStxAddressChain(mainnet ? ChainID.Mainnet : ChainID.Testnet, derivationPath)(masterKeychain) const uncompressed_hex = bitcoin.ECPair.fromPublicKey( Buffer.from(keys.publicKey, 'hex'), @@ -90,7 +84,8 @@ async function generateKeys(seed_phrase, mainnet) { stacking: `{ hashbytes: 0x${c32c.c32addressDecode(keys.address)[1]}, version: 0x00 }`, btc: c32c.c32ToB58(keys.address), wif: privateKeyToWIF(keys.privateKey, mainnet), - } + derivationPath: derivationPath + } } yargs @@ -113,6 +108,16 @@ yargs default: 24, describe: 'Use 24 or 12 secret phrase', }) + .option('path', { + alias: 'd', + describe: 'The derivation path for network, e.g. "m/44\'/5757\'/0\'/0/0"', + type: 'string', + }) + .option('account', { + alias: 'a', + describe: 'Specify the account number for the derivation path', + type: 'number', + }) .choices('w', [12, 24]) .command('sk', 'Generate keys for Stacks 2.0', (yargs) => { yargs.positional('sk', { @@ -125,7 +130,19 @@ yargs const phrase = argv.phrase || generateMnemonic(entropy, randombytes) // console.log('generate', phrase, argv.testnet) - console.log(JSON.stringify(await generateKeys(phrase, mainnet), null, 2)) + let derivationPath; + if (argv.path && argv.account) { + console.error('Error: Only one of --path or --account can be used, not both.'); + process.exit(1); + } else if (argv.path) { + derivationPath = argv.path; + } else if (argv.account) { + derivationPath = `m/44'/5757'/${argv.account}'/0/0`; + } else { + derivationPath = `m/44'/5757'/0'/0/0`; // default derivation path + } + + console.log(JSON.stringify(await generateKeys(phrase, mainnet, derivationPath), null, 2)) }) .command('pk ', 'Generate keys for Stacks 2.0 from private key', (yargs) => { yargs.positional('key', { @@ -153,8 +170,8 @@ yargs wif: privateKeyToWIF(ec_pair_compressed.privateKey, mainnet), }, null, 2)) }) - .command('uncompress_pubpkey', 'Uncompress a public key', (yargs) => { - yargs.positional('uncompress_pubpkey', { + .command('uncompress_pubkey ', 'Uncompress a public key', (yargs) => { + yargs.positional('key', { type: 'string', describe: 'Uncompress a public key' }) @@ -170,6 +187,8 @@ yargs .demandCommand(1, 'You need at least one command') .requiresArg('w') .requiresArg('p') + .requiresArg('d') + .requiresArg('a') .strictCommands(true) .strictOptions(true) .help()