Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support networkDerivationPath and account as args #4

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 34 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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')
}
Expand Down Expand Up @@ -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'),
Expand All @@ -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
Expand All @@ -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', {
Expand All @@ -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 <key>', 'Generate keys for Stacks 2.0 from private key', (yargs) => {
yargs.positional('key', {
Expand Down Expand Up @@ -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 <key>', 'Uncompress a public key', (yargs) => {
yargs.positional('key', {
type: 'string',
describe: 'Uncompress a public key'
})
Expand All @@ -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()
Expand Down