Comparing version 0.3.1 to 3.0.0
{ | ||
"name": "dashhd", | ||
"version": "0.3.1", | ||
"description": "Browser, Node, Bundler, and CLI compatible Dash HD Wallet tools", | ||
"main": "index.js", | ||
"bin": { | ||
"dash-mnemonic-generate": "./bin/mnemonic-generate.js", | ||
"dash-mnemonic-to-addrs": "./bin/mnemonic-to-addrs.js", | ||
"dash-mnemonic-to-seed": "./bin/mnemonic-to-seed.js", | ||
"dash-mnemonic-to-wifs": "./bin/mnemonic-to-wifs.js", | ||
"dash-mnemonic-to-xprv": "./bin/mnemonic-to-xprv.js", | ||
"dash-mnemonic-to-xpub": "./bin/mnemonic-to-xpub.js", | ||
"dash-seed-to-wif": "./bin/seed-to-wif.js", | ||
"dash-seed-to-wifs": "./bin/seed-to-wifs.js", | ||
"dash-seed-to-xkeys": "./bin/seed-to-xkeys.js", | ||
"dash-qr": "./bin/qr.js", | ||
"dash-wif-to-qr": "./bin/wif-to-qr.js", | ||
"dash-xprv-to-wif": "./bin/xprv-to-wif.js", | ||
"dash-xprv-to-wifs": "./bin/xprv-to-wifs.js", | ||
"dash-xpub-to-addr": "./bin/xpub-to-addr.js", | ||
"dash-xpub-to-addrs": "./bin/xpub-to-addrs.js" | ||
"version": "3.0.0", | ||
"description": "Manage HD Keys from HD Wallet Seed and Extended (xprv, xpub) Key Paths. Part of $DASH Tools.", | ||
"main": "dashhd.js", | ||
"browser": { | ||
"crypto": false | ||
}, | ||
"files": [ | ||
"bin/", | ||
"dashd.js", | ||
"index.js", | ||
"lib/" | ||
], | ||
"scripts": { | ||
"test": "node test.js", | ||
"doc": "npx jsdoc@3.x --configure ./jsdoc.conf.json --destination ./docs --package ./package.json --readme ./README.md --access all --private --recurse ./lib/", | ||
"fmt": "npx -p prettier@2.x -- prettier -w '**/*.{js,md}'", | ||
"lint": "npx -p typescript@4.x -- tsc -p ./jsconfig.json", | ||
"prepublish": "npx -p jswt@1.x -- reexport", | ||
"version": "npm version -m \"chore(release): bump to v%s\"" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/dashhive/dashhd.js.git" | ||
"url": "git://github.com/dashive/dashhd.js" | ||
}, | ||
"license": "SEE LICENSE IN LICENSE", | ||
"keywords": [ | ||
"bip39", | ||
"DASH", | ||
"hd", | ||
"wallet", | ||
"key", | ||
"keys", | ||
"path", | ||
"bip32", | ||
"bip44", | ||
"mnemonic", | ||
"seed", | ||
"passphrase", | ||
"wallet", | ||
"hd", | ||
"dash" | ||
"hierarchical", | ||
"deterministic", | ||
"crypto", | ||
"$DASH", | ||
"Tools" | ||
], | ||
"author": "AJ ONeal <aj@therootcompany.com> (https://throotcompany.com/)", | ||
"license": "SEE LICENSE IN LICENSE", | ||
"bugs": { | ||
"url": "https://github.com/dashhive/dashhd.js/issues" | ||
"url": "https://github.com/dashive/dashhd.js/issues" | ||
}, | ||
"homepage": "https://github.com/dashhive/dashhd.js#readme", | ||
"homepage": "https://github.com/dashive/dashhd.js", | ||
"files": [ | ||
"dashhd.js", | ||
"jsconfig.json" | ||
], | ||
"devDependencies": { | ||
"@dashincubator/secp256k1": "^1.7.1-4", | ||
"dashphrase": "^1.3.1", | ||
"mocha": "^10.2.0", | ||
"mocha-lcov-reporter": "0.0.1" | ||
}, | ||
"dependencies": { | ||
"@dashincubator/base58check": "^1.3.1", | ||
"@dashincubator/ripemd160": "^2.3.0", | ||
"bip39": "^3.0.4", | ||
"hdkey": "github:dashhive/hdkey", | ||
"qrcode-svg": "^1.1.0" | ||
"dashkeys": "^1.0.0" | ||
}, | ||
"scripts": { | ||
"lint": "npx -p typescript@4.x -- tsc -p ./jsconfig.json", | ||
"browser-test": "npx mochify@6 --wd -R spec", | ||
"test": "mocha", | ||
"unit": "mocha", | ||
"coverage": "npx istanbul@0.4 cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", | ||
"coveralls": "npm run-script coverage && npx coveralls@3 < coverage/lcov.info", | ||
"bump": "npm version -m \"chore(release): bump to v%s\"", | ||
"fmt": "npx -p prettier@2.x -- prettier -w '**/*.{js,md}'" | ||
} | ||
} |
1363
README.md
@@ -1,259 +0,1298 @@ | ||
# dashhd.js | ||
# [DashHD.js](https://github.com/dashhive/dashhd.js) | ||
Browser, Node, Bundler, and CLI compatible Dash HD Wallet tools | ||
Manage HD Keys from HD Wallet Seed and Extended (xprv, xpub) Key Paths. \ | ||
(compatible with the [Hierarchical Deterministic Keys (BIP-44)][bip-44] and [BIP-32][bip-32] | ||
specs) | ||
> A fully-functional, production-ready reference implementation of Dash HD - | ||
> suitable for learning DASH specs and protocols, and porting to other | ||
> languages. | ||
```text | ||
HD Wallet Seed: ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069 | ||
(the canonical _Zeed_ seed, from the canonical _Zoomonic_) | ||
``` | ||
```text | ||
HD XKey Path: m/44'/5'/0'/0 | ||
XPrv: xprvA2L7qar7dyJNhxnE47gK5J6cc1oEHQuAk8WrZLnLeHTtnkeyP4w6 | ||
Eo6Tt65trtdkTRtx8opazGnLbpWrkhzNaL6ZsgG3sQmc2yS8AxoMjfZ | ||
XPub: xpub6FKUF6P1ULrfvSrhA9DKSS3MA3digsd27MSTMjBxCczsfYz7vcFL | ||
nbQwjP9CsAfEJsnD4UwtbU43iZaibv4vnzQNZmQAVcufN4r3pva8kTz | ||
HD Key Path: m/44'/5'/0'/0/0 | ||
WIF: XCGKuZcKDjNhx8DaNKK4xwMMNzspaoToT6CafJAbBfQTi57buhLK | ||
Address: XrZJJfEKRNobcuwWKTD3bDu8ou7XSWPbc9 | ||
``` | ||
```text | ||
HD XKey Path: m/44'/5'/1'/1 | ||
XPrv: xprvA2ACWaqwADRtbkLsM6oQHzeWtqZVviBmKMKNRBFcwKGGRBgWHNeo | ||
ZSKzduFMFkqvNsV5LaqRT9XRibzgSAweAAsfMF35PWy6beK3aL1BwTU | ||
XPub: xpub6F9Yv6NpzazBpERLT8LQf8bFSsPzLAucgaEyDZfEVeoFHz1epuy4 | ||
7EeUVCRTNVToM1zgFZMxiGs2AFc9cNqZE2UVwJod2zPkG7W4ZGRuwJJ | ||
HD Key Path: m/44'/5'/1'/1/1 | ||
WIF: XF9murLtNpJaZXbwMxqJ6BhigEtu9NxfBCJDBokCJcqFkYkz3itz | ||
Address: XueHW2ELMxoXzXcaHMxmwVWhcADE1W5s8c | ||
``` | ||
[bip-32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki | ||
[bip-39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki | ||
[bip-43]: https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki | ||
[bip-44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki | ||
[dash-phrase]: https://github.com/dashhive/DashPhrase.js | ||
[dash-keys]: https://github.com/dashhive/DashKeys.js | ||
# Table of Contents | ||
- CLI utils | ||
- dash-mnemonic-generate | ||
- dash-mnemonic-to-addrs (salted or plain) | ||
- dash-mnemonic-to-seed | ||
- dash-mnemonic-to-wifs | ||
- dash-mnemonic-to-xprv | ||
- dash-mnemonic-to-xpub | ||
- dash-seed-to-xkeys (extended public / private keys) | ||
- dash-seed-to-wif | ||
- dash-seed-to-wifs | ||
- dash-qr (converts XPubs and PayAddrs, and XPrvs and WIFs to QR Codes) | ||
- dash-xprv-to-wif | ||
- dash-xprv-to-wifs | ||
- dash-xpub-to-addr | ||
- dash-xpub-to-addrs | ||
- Test Fixtures | ||
- Mnemonic | ||
- Salted Seed (password) | ||
- Plain Seed | ||
- [Install](#install) | ||
- Node, Bun, & Bundlers | ||
- Browser | ||
- [Usage Overview](#usage-overwiew) | ||
- [**Production-Ready** QuickStart](#production-quickstart) | ||
- [API](#api) | ||
- Note on derivation exceptions | ||
- DashHd derivations & encodings | ||
- HD Key Types | ||
- [Tutorial Walkthrough](#walkthrough) | ||
- Passphrase, Secret, Seed | ||
- Wallet Derivation | ||
- HD Path Derivation | ||
- _Wallet_, _Account_, and _X Key_ Derivation | ||
- XPrv and XPub Encoding | ||
- _Address Key_ Derivation | ||
- WIF and Address Encoding | ||
- [Glossary](#glossary) | ||
# CLI | ||
# Install | ||
## Install | ||
Notes: | ||
- **`secp256k1`** is **not a listed dependency** in `package.json` for this - \ | ||
because there are many decent implementations to choose from. \ | ||
HOWEVER, [DashKeys][dash-keys] will use `@dashincubator/secp256k1@1.x` \ | ||
by default, it if is installed. \ | ||
See [DashKeys][dash-keys] if you prefer to use another implementation. | ||
- [DashPhrase][dash-phrase] is recommended, but not strictly required. \ | ||
(you can work with bare `seed`s without a word list) | ||
## Node, Bun, & Bundlers | ||
```sh | ||
npm install --location=global dashhd | ||
npm install --save @dashincubator/secp256k1@1.x | ||
npm install --save dashhd@3.x | ||
npm install --save dashphrase | ||
``` | ||
Or, use without installing: | ||
```js | ||
let DashHd = require("dashd"); | ||
let DashPhrase = require("dashphrase"); | ||
``` | ||
```sh | ||
npx -p dashd dash-mnemonic-generate | ||
## Browser | ||
```html | ||
<script src="https://unpkg.com/@dashincubator/secp256k1.js"></script> | ||
<script src="https://unpkg.com/dashkeys@0.9/dashkeys.js"></script> | ||
<script src="https://unpkg.com/dashhd@3.x/dashhd.js"></script> | ||
``` | ||
## Usages | ||
```js | ||
let DashHd = window.DashHd; | ||
let DashPhrase = window.DashPhrase; | ||
``` | ||
```sh | ||
./bin/mnemonic-generate.js | ||
# Usage Overview | ||
# again cable air agree veteran march surface dragon behind isolate just wreck | ||
1. Generate a _Wallet_ | ||
```js | ||
let wallet = await DashHd.fromSeed(seedBytes); | ||
``` | ||
- As a one-off, you can **directly** generate an _Address Key_ by _HD Path_ | ||
```js | ||
let hdpath = `m/44'/5'/0'/0/0`; | ||
let key = await DashHd.derivePath(hdpath); | ||
let wif = await DashHd.toWif(key.privateKey); | ||
let address = await DashHd.toAddr(key.publicKey); | ||
``` | ||
2. Generate an _Account_ | ||
```js | ||
let accountIndex = 0; | ||
let account = await wallet.deriveAccount(accountIndex); | ||
``` | ||
3. Generate an _X Key_ (Extended Private or Public Key) | ||
```js | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
``` | ||
4. (Optional) Generate _XPrv_ and _XPubs_ | ||
```js | ||
let xprv = DashHd.toXPrv(xkey); | ||
let xpub = DashHd.toXPub(xkey); | ||
``` | ||
5. Generate an _Address Key_ | ||
```js | ||
let key = await xkey.deriveAddress(use); | ||
``` | ||
6. Generate _WIF_ & _Address_ | ||
```js | ||
let wif = await DashHd.toWif(key.privateKey); | ||
let address = await DashHd.toAddr(key.publicKey); | ||
``` | ||
# Production QuickStart | ||
However, production code will look more like this: | ||
1. Get a _Seed_ from the user's _Passphrase Mnemonic_ and _Secret Salt_ | ||
```js | ||
let wordList = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; | ||
let secretSalt = "TREZOR"; | ||
// Derive a Wallet Seed | ||
let seedBytes = await DashPhrase.toSeed(wordList, secretSalt); | ||
``` | ||
2. Derive a _Wallet_, _Account_, and _X Key_, if possible. \ | ||
(reject the _Passphrase_ or _Seed_ if _Account_ index 0 is not valid) | ||
```js | ||
let accountIndex = 0; | ||
let xkey; | ||
try { | ||
let wallet = await DashHd.fromSeed(seedBytes); // seed from step 1 | ||
let account = await wallet.deriveAccount(accountIndex); | ||
void (await account.deriveXKey(DashHd.CHANGE)); | ||
xkey = await account.deriveXKey(DashHd.RECEIVE); | ||
} catch (e) { | ||
window.alert("Error: The passphrase can't generate a valid 1st account!"); | ||
} | ||
``` | ||
**Note**: For multi-*Account*s apps, just mark failed indexes as invalid. | ||
3. Derive a batch of _Address_ keys (20 is the typical number) | ||
```js | ||
let keys = []; | ||
let previousIndex = 0; | ||
let use = DashHd.RECEIVE; | ||
let last = previousIndex + 20; | ||
for (let i = previousIndex; i < last; i += 1) { | ||
let key; | ||
try { | ||
key = await xkey.deriveAddress(i); // xkey from step 2 | ||
} catch (e) { | ||
last += 1; | ||
continue; | ||
} | ||
let wif = await DashHd.toWif(key.privateKey); | ||
let address = await DashHd.toAddr(key.publicKey); | ||
let hdpath = `m/44'/5'/${accountIndex}'/${use}/${i}`; | ||
keys.push({ | ||
index: i, | ||
hdpath: hdpath, // useful for multi-account indexing | ||
address: address, // XrZJJfEKRNobcuwWKTD3bDu8ou7XSWPbc9 | ||
wif: wif, // XCGKuZcKDjNhx8DaNKK4xwMMNzspaoToT6CafJAbBfQTi57buhLK | ||
}); | ||
} | ||
``` | ||
Note: it may be useful to indicate the path of the | ||
# API | ||
- IMPORTANT | ||
- _all_ derivations _can_ fail | ||
- DashHd | ||
```js | ||
Constants | ||
MAINNET, TESTNET | ||
HARDENED, PUBLIC | ||
RECEIVE, CHANGE | ||
async fromSeed(seedBytes, opts) // depth-0 hdkey (Wallet) | ||
async fromXKey(xprv||xpub, opts) // depth-4 hdkey (XKey) | ||
async toWif(privBytes, opts) | ||
async toAddr(pubBytes, opts) | ||
async toXPrv(xkey, opts) | ||
async toXPub(xkey, opts) | ||
``` | ||
- DashHd (BIP-32) | ||
```js | ||
create(hdkey) // depth-n HDKey (any) | ||
async derivePath( | ||
hdkey, | ||
hdpath, | ||
) | ||
async deriveChild( | ||
hdkey, | ||
index, | ||
isHardened, | ||
) | ||
``` | ||
- HD Key Types | ||
```js | ||
Wallet | ||
async deriveAccount(accountIndex) | ||
Account | ||
async deriveXKey(use = 0) | ||
XKey | ||
async deriveAddress(keyIndex) | ||
Key | ||
{ privateKey, publicKey } | ||
HDKey | ||
{ versions, depth, parentFingerprint, | ||
index, chainCode, privateKey, publicKey } | ||
``` | ||
## IMPORTANT | ||
All _derive_ methods can fail (throw an error). | ||
This is _highly-unlikely_, but _normal_ (because maths). | ||
It's recommended that: | ||
1. **Before accepting a seed as valid**, derive these HD Paths: | ||
``` | ||
m/44'/5'/0'/0/0 | ||
m/44'/5'/0'/0/1 | ||
m/44'/5'/0'/1/0 | ||
m/44'/5'/0'/1/1 | ||
``` | ||
If derivation fails, **reject the seed**. | ||
2. When creating a **new Account Index**, always **derive both *Use Index*es**: | ||
``` | ||
m/44'/5'/n'/0 | ||
m/44'/5'/n'/1 | ||
``` | ||
If either the _Account Index_ or _Use Index_ derivation fails, \ | ||
**mark the whole Account Index as invalid**. | ||
3. If a key derivation ever fails, simply **mark that key as invalid**. | ||
``` | ||
m/44'/5'/0'/0/n | ||
``` | ||
Most likely you will never personally encounter this failure. | ||
However, when your software is generating millions and billions of keys across | ||
thousands and millions of accounts, eventually one of the key expansion hashes | ||
will _just so happen_ to hit _Infinity_ or _Zero_ on the curve. | ||
Some libraries abstract this away by automatically incrementing to the next | ||
index on failure, but since the occurrence is so rare, and therefore will | ||
inevitably lead to highly confusing and difficult to track bugs (including | ||
address re-use, a privacy concern), we do not. | ||
## `DashHd` | ||
This is the top-level export, and these methods are specific for the BIP-44 use | ||
case of HD Keys for: | ||
- Wallet | ||
- Account | ||
- XKey (XPrv or XPub) | ||
- Key (WIF or Address) | ||
### Constants | ||
**`MAINNET`**, **`TESTNET`** | ||
The `version` byte that gets base58check encoded to a human-friendly prefix. | ||
```js | ||
// "xprv" "xpub" | ||
DashHd.MAINNET = { private: 0x0488ade4, public: 0x0488b21e }; | ||
``` | ||
```sh | ||
./bin/mnemonic-to-addrs.js | ||
# Usage: mnemonic-to-addrs <./mnemonic.txt> [./passphrase.txt] [startPath] [endPath] | ||
```js | ||
// "tprv" "tpub" | ||
DashHd.TESTNET = { private: 0x0488ade4, public: 0x0488b21e }; | ||
``` | ||
# m/44'/5'/0'/0/0: | ||
# XjxyR1gve94LuKqkMLEeqJbEVM5B5q1ZSx | ||
**`HARDENED`**, **`PUBLIC`** | ||
Whether the HD Key should be derived as _Hardened_ or _Public_ (sharable). | ||
```js | ||
DashHd.HARDENED = true; | ||
DashHd.PUBLIC = false; | ||
``` | ||
```sh | ||
./bin/mnemonic-to-seed.js | ||
# Usage: mnemonic-to-seed <./mnemonic.txt> [./passphrase.txt] | ||
**`RECEIVE`**, **`CHANGE`** | ||
# f3f1ff73a93aa5b2fa5db7bdabc184e26bd5120fac3345d89133b3e027982f3d5a7b02704b7f03142873bb264498676798dbefa86ff63f18f14d12e61d114be4 | ||
The intended use of the key - as a receiving address for external funds, or an | ||
internal change address. | ||
```js | ||
DashHd.RECEIVE = 0; | ||
DashHd.CHANGE = 1; | ||
``` | ||
```sh | ||
./bin/mnemonic-to-wifs.js | ||
# Usage: mnemonic-to-wifs <./mnemonic.txt> [./passphrase.txt] [startPath] [endPath] | ||
### `fromSeed(seedBytes, opts)` | ||
# m/44'/5'/0'/0/0: XKHiWYkmDkNnWGP756UCGcuZ21mHGeYdWeCBBHCBGZaf3NYw1SAz | ||
# XjxyR1gve94LuKqkMLEeqJbEVM5B5q1ZSx | ||
Generate a [Wallet](#wallet) (depth-0 HD Key) \ | ||
with a default HD Path prefix of `m/44'/5'`. | ||
```js | ||
let words = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; | ||
let secret = "TREZOR"; | ||
let seed = await DashPhrase.toSeed(words, secret); | ||
``` | ||
```sh | ||
./bin/mnemonic-to-xprv.js | ||
# Usage: mnemonic-to-xprv <./mnemonic.txt> [./passphrase.txt] [hdpath] | ||
```js | ||
let wallet = await DashHd.fromSeed(seedBytes, options); | ||
// { deriveAccount, | ||
// versions, | ||
// depth: 0, parentFingerprint: 0, index: 0, | ||
// chainCode, privateKey, publicKey } | ||
``` | ||
# xprvA1WUDWFxdE5UGW1XNTnkZnd3K6bdidZBTtzvtEQziBpS3N8tajC4QKyRLmas7DK4HXK76wSXgMV1uV6RbKyM5f4uu1VmguEhAqvzQwr2mrC | ||
# xpub6EVpd1nrTbdmUz5zUVKkvvZms8S886H2q7vXgcpcGXMQvAU38GWJx8HuC28Bm8Cizq7dHJvL6armkvL7vvxRpxUxAmpVQF6s8aq5BRBCMrD | ||
**Options** | ||
```json5 | ||
{ | ||
purpose: 44, // BIP-44 (default) | ||
coinType: 5, // DASH (default) | ||
versions: DashHd.MAINNET, // default | ||
} | ||
``` | ||
```sh | ||
./bin/mnemonic-to-xpub.js | ||
# Usage: mnemonic-to-xpub <./mnemonic.txt> [./passphrase.txt] [hdpath] | ||
### `fromXKey(xprv || xpub, opts)` | ||
# xpub6EVpd1nrTbdmUz5zUVKkvvZms8S886H2q7vXgcpcGXMQvAU38GWJx8HuC28Bm8Cizq7dHJvL6armkvL7vvxRpxUxAmpVQF6s8aq5BRBCMrD | ||
Parses a Base58Check-encoded `xprv` or `xpub` (Extended Private or Public Key) | ||
into an _X Key_ (depth-4 HD Key). | ||
Only the _depth_ is known. The prefix is **_not_** recoverable, but can be | ||
assumed to be something like `m/44'/5'/0'/0`. | ||
```js | ||
let xprv = | ||
"xprvA2L7qar7dyJNhxnE47gK5J6cc1oEHQuAk8WrZLnLeHTtnkeyP4w6Eo6Tt65trtdkTRtx8opazGnLbpWrkhzNaL6ZsgG3sQmc2yS8AxoMjfZ"; | ||
``` | ||
```sh | ||
./bin/seed-to-xkeys.js | ||
# Usage: seed-to-wif <./seed.hex> [account] [direction] | ||
```js | ||
let xkey = await DashHd.fromXKey(xkeyString, options); | ||
// { deriveAddress, | ||
// versions: v, depth: n, parentFingerprint: p, index: 0, | ||
// chainCode: c, privateKey: privOrNull, publicKey: pubBytes } | ||
``` | ||
# xprvA1uHCfBAez3GCjbyU8UWQitSaZ3RUUy8eft7mf8rnH5z6WCQJ6ehHTNAPRes6k6cwimXjhEHoxka79uoQ2Kdyx7BxbGYKGSnkdjfdjXfvjr | ||
# xpub6EtdcAi4VMbZRDgSaA1WmrqB8asuswgz1toia3YULccxyJXYqdxwqFgeEexVxr8ytJPHZYTrhbYJjqaFumih45awabyaHwUmCvXbGf7sujG | ||
**Options** | ||
```json5 | ||
{ | ||
bip32: false, // allow arbitrary depth (Wallet, Purpose, Coin, Account, etc) | ||
normalizePublicKey: false, // validate pubkey by re-ploting X,Y on curve | ||
versions: DashHd.MAINNET, // must match the given version | ||
} | ||
``` | ||
```sh | ||
./bin/seed-to-wif.js | ||
# Usage: seed-to-wif <./seed.hex> [account] [direction] [index] | ||
### `toWif(privBytes, opts)` | ||
Wrapper around `DashKeys.encodeKey(keyBytes, options)` to Base58Check-encode a | ||
Private Key as in _Wallet Import Format_. | ||
```js | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
``` | ||
```sh | ||
./bin/xprv-to-wif.js | ||
# Usage: xprv-to-wif <./xprv.txt> [index] | ||
```js | ||
let wif = await DashHd.toWif(key.privateKey); | ||
// "XCGKuZcKDjNhx8DaNKK4xwMMNzspaoToT6CafJAbBfQTi57buhLK" | ||
``` | ||
```txt | ||
XD3sNsdXjXvsnGtcbiqj3SCVdGHyHCRWFDaCAhWotxVfudSN4iRt | ||
**Options** | ||
```json5 | ||
{ | ||
version: "cc", | ||
} | ||
``` | ||
```sh | ||
./bin/xpub-to-addr.js | ||
# Usage: xpub-to-addr <./xpub.txt> [index] | ||
### `toAddr(pubBytes, opts)` | ||
Wrapper around `DashKeys.encodeKey(keyBytes, options)` to Base58Check-encode a | ||
Public Key as an _Address_. | ||
```js | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
``` | ||
```txt | ||
XnRtALP7ns8stH6o79RQTiWGeW2SQeetxL | ||
```js | ||
let wif = await DashHd.toAddr(key.publicKey); | ||
// "XrZJJfEKRNobcuwWKTD3bDu8ou7XSWPbc9" | ||
``` | ||
```sh | ||
./bin/seed-to-wifs.js | ||
# Usage: seed-to-wif <./seed.hex> [fromPath] [toPath] | ||
# Example: seed-to-wif ./seed.hex "0'/0/0" "1'/0/1" | ||
**Options** | ||
```json5 | ||
{ | ||
version: "4c", // (default) DASH PubKeyHash | ||
} | ||
``` | ||
```sh | ||
./bin/xprv-to-wifs.js | ||
# Usage: xprv-to-wif <./xprv.txt> [fromPath] [toPath] | ||
# Example: xprv-to-wif ./xprv.txt "0'/0/0" "1'/0/1" | ||
### `toXPrv(hdkey)` | ||
Wrapper around `DashKeys.encodeKey(keyBytes, options)` to Base58Check-encode an | ||
_X Key_ as an _XPrv_. | ||
```js | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
// Or... | ||
let xkey = await DashHd.fromXKey(xprv); | ||
``` | ||
```txt | ||
m/44'/5'/0'/0/0: XKHiWYkmDkNnWGP756UCGcuZ21mHGeYdWeCBBHCBGZaf3NYw1SAz | ||
XjxyR1gve94LuKqkMLEeqJbEVM5B5q1ZSx | ||
m/44'/5'/0'/0/1: XCsy8Qw1fLH7C1UxLjBfTfLpn8DMRK1TMNNE2a5J1F4TyE5UApcK | ||
XxRrwh1xBWig9rfLyiy494u2vj6YXQMsH7 | ||
```js | ||
let xprv = await DashHd.toXPrv(xkey); | ||
// "xprvA2L7qar7dyJNhxnE47gK5J6cc1oEHQuAk8WrZLnLeHTtnkeyP4w6Eo6Tt65trtdkTRtx8opazGnLbpWrkhzNaL6ZsgG3sQmc2yS8AxoMjfZ" | ||
``` | ||
m/44'/5'/1'/0/0: XEWDJiCKuSaNHUoYFaGiTv1QG3p49xc4vx6cbS19CHZFe1TpwvJF | ||
XrX7Ph3rXV9kyzQfcDteCf14xvm8nM5Mmg | ||
m/44'/5'/1'/0/1: XHTyj295ekRT4UqDnYd5zdHwLZ5iJdQnN8DjH9CQqPxFVipJqYns | ||
XxTRzgDwnED8cPmrnCRRwpnnT3v5VL3KcE | ||
### `toXPrv(hdkey)` | ||
Wrapper around `DashKeys.encodeKey(keyBytes, options)` to Base58Check-encode an | ||
_X Key_ as an _XPub_. | ||
```js | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
// Or... | ||
let xkey = await DashHd.fromXKey(xpub); | ||
``` | ||
```sh | ||
./bin/xpub-to-addrs.js | ||
# Usage: xpub-to-addrs <./xpub.txt> [startIndex] [endIndex] | ||
# Example: xpub-to-addrs ./xpub.txt 0 1 | ||
```js | ||
let xpub = await DashHd.toXPub(xkey); | ||
// "xpub6FKUF6P1ULrfvSrhA9DKSS3MA3digsd27MSTMjBxCczsfYz7vcFLnbQwjP9CsAfEJsnD4UwtbU43iZaibv4vnzQNZmQAVcufN4r3pva8kTz" | ||
``` | ||
```txt | ||
XjxyR1gve94LuKqkMLEeqJbEVM5B5q1ZSx | ||
XxRrwh1xBWig9rfLyiy494u2vj6YXQMsH7 | ||
## `DashHd` (BIP-32) | ||
These are the more rudimentary functions which can be used for either style of | ||
HD Keys: | ||
- BIP-44 / BIP-43 (Wallet, Account, XKey, Key) | ||
- or BIP-32 (generic HD Keys) | ||
These do not enforce BIP-44 compliance, so they can be useful for testing and | ||
debugging. | ||
### `create(hdkey)` | ||
Returns an _HD Key_ with any missing values set to their default. | ||
```js | ||
let hdkey = DashHd.create({ | ||
chainCode: chainBytes, | ||
privateKey: privOrNull, | ||
publicKey: pubBytes, | ||
}); | ||
``` | ||
# Fixtures | ||
**HD Key** | ||
For the purpose of testing against known-good values, we provide these fixtures. | ||
```json5 | ||
{ | ||
versions: DashHd.MAINNET, | ||
depth: 0, | ||
parentFingerprint: 0, // should be set if depth is set | ||
index: 0, | ||
chainCode: chainBytes, // required | ||
privateKey: privOrNull, // required (or null) | ||
publicKey: pubBytes, // required | ||
} | ||
``` | ||
## Mnemonic | ||
### `deriveChild(hdkey, index, isHardened)` | ||
This mnemonic can be used to generate any number of seeds, meaning any number of | ||
"password-protected" wallets. | ||
Derives one additional depth of an HD Key, by index value and hardness. | ||
```txt | ||
again cable air agree veteran march surface dragon behind isolate just wreck | ||
```js | ||
let hdkey = DashHd.fromXKey(xpub, { bip32: true }); | ||
``` | ||
**Misnomer Alert**: this is obviously a _passphrase_, but early on the term | ||
_passphrase_ was mistakenly used to describe a _salt_, so we're stuck calling | ||
this "mnemonic". | ||
```js | ||
let UNHARDENED = false; | ||
let nextIndex = 42; | ||
let nextHdkey = await DashHd.deriveChild(hdkey, nextIndex, UNHARDENED); | ||
``` | ||
## Seed, xprv, WIFs, & Addrs (Empty Password) | ||
### `derivePath(hdkey, hdpath)` | ||
If the mnemonic is used _without_ a "password" (or "secret"), it will produce | ||
this HD Wallet "seed": | ||
Iterates over an HD Path, calling `deriveChild(hdkey, index, isHardened)` for | ||
each index. | ||
```txt | ||
f3f1ff73a93aa5b2fa5db7bdabc184e26bd5120fac3345d89133b3e027982f3d5a7b02704b7f03142873bb264498676798dbefa86ff63f18f14d12e61d114be4 | ||
Can derive any valid BIP-32 path, at any depth. \ | ||
(even if nonsensical - such as a hardened key after an unhardened one) | ||
```js | ||
let hdkey = DashHd.fromSeed(seedBytes); | ||
``` | ||
The `xprv` and `xpub` for the HD prefixes `m/44'/5'/0'/0` and `m/44'/5'/2'/1` | ||
are: | ||
```js | ||
let childKey = DashHd.derivePath(hdkey, `m/0'/0/1'/1/0`); | ||
// { versions, depth, parentFingerprint, | ||
// index, chainCode, privateKey, publicKey } | ||
``` | ||
```sh | ||
# m/44'/5'/0'/0 | ||
xprvA1uHCfBAez3GCjbyU8UWQitSaZ3RUUy8eft7mf8rnH5z6WCQJ6ehHTNAPRes6k6cwimXjhEHoxka79uoQ2Kdyx7BxbGYKGSnkdjfdjXfvjr | ||
This is the same as | ||
xpub6EtdcAi4VMbZRDgSaA1WmrqB8asuswgz1toia3YULccxyJXYqdxwqFgeEexVxr8ytJPHZYTrhbYJjqaFumih45awabyaHwUmCvXbGf7sujG | ||
```js | ||
let childKey = hdkey; | ||
childKey = await DashHd.deriveChild(childKey, 0, true); | ||
childKey = await DashHd.deriveChild(childKey, 0, false); | ||
childKey = await DashHd.deriveChild(childKey, 1, true); | ||
childKey = await DashHd.deriveChild(childKey, 1, false); | ||
childKey = await DashHd.deriveChild(childKey, 0, false); | ||
// { versions, depth, parentFingerprint, | ||
// index, chainCode, privateKey, publicKey } | ||
``` | ||
```sh | ||
# m/44'/5'/2'/1/2 | ||
xprvA1zEQq1FvKbQkXosnKgsBTrtk1K6iEfdUSLnHgf7ejLYU2NAU9mK5gqAYNP34ykNMfVkY4emcdTjuaqUmz2J7Hohupn9VFRhQrV6CWpmKaZ | ||
## Key Types | ||
xpub6EyapLY9kh9hy1tLtMDsYbodJ39b7hPUqfGP654jD4sXLphK1h5ZdV9ePgapU2jMrVBy4sXUW4CSxG3aXdDgJGTsMQFy8D51TRSdjcjQxpV | ||
A BIP-44 _HD Path_ has 5 depths, each with their own HD Key Type: | ||
```js | ||
//0 1 2 3 4 5 | ||
`m/${purpose}'/${coin}'/${account}'/${use}/${index}`; | ||
``` | ||
Given the HD paths `m/44'/5'/0'/0/0` and `m/44'/5'/2'/1/2`, that seed will | ||
produce these WIFs (Private Keys) and Pay Addresses (PubKey Hashes). | ||
- Depth 0: _Wallet (Root) Key_ - calculated from a Seed | ||
- Depth 1: _Purpose Key_ - predefined as index `44'` for BIP-44 | ||
- Depth 2: _Coin Key_ - predefined as index `5'` for DASH \ | ||
(this is also sometimes referred to as the "Wallet") | ||
- Depth 3: _Account Key_ - derived by account index | ||
- Depth 4: _X Key_ is for sharing, derived by Use (Receiving or Change) | ||
- Depth 5: _Key_ is for paying and getting paid (WIF or Address) | ||
- Depth 6+: not defined by _BIP-44_, application specific | ||
```txt | ||
(account 0, external/receiving, address 0) | ||
m/44'/5'/0'/0/0: XD3sNsdXjXvsnGtcbiqj3SCVdGHyHCRWFDaCAhWotxVfudSN4iRt (WIF) | ||
XnRtALP7ns8stH6o79RQTiWGeW2SQeetxL (Addr) | ||
m/44'/5'/0'/0/1: XJ8RCLHTg55mBbvv5mF5Ja3DikzBYWxHn5DvKyJoFm2cufBdUSc2 | ||
XeGtfXhcgeyLS2EtAnrkbEKNmVVDupZV6p | ||
Keys can be derived | ||
(account 2, internal/change, address 2) | ||
m/44'/5'/2'/1/2: XJDdTJ1WigKiUdsvofPGmMCHoPd4kpWKD1yGrigJXuwqcxoLUn4W (WIF) | ||
XoMEcuxW4Ki1SXduDjQUdntSYU4PWzhqTC (Addr) | ||
- by instance (recommended): | ||
```js | ||
let wallet = await DashHd.fromSeed(seedBytes); | ||
let accountIndex = 0; | ||
let account = await wallet.deriveAccount(accountIndex); | ||
let use = DashHD.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
``` | ||
- by path (not for loops): | ||
```js | ||
let wallet = await DashHd.fromSeed(seedBytes); | ||
let hdpath = `m/${purpose}'/${coin}'/${account}'/${use}/${index}`; | ||
let key = await DashHd.derivePath(wallet, hdpath); | ||
``` | ||
The benefit of deriving _by instance_ is that when you need to derive in a loop, | ||
such as multiple keys at the same depth - e.g. Addresses from the same X Key, or | ||
Accounts from the same Wallet - you're not deriving from the Wallet Root each | ||
time. | ||
Since the key derivation process is intentionally somewhat slow, it's best to | ||
loop at the lowest level that you can. For example: | ||
```js | ||
// ... | ||
let xkey = await account.deriveXKey(use); | ||
for (let addressIndex = 0; addressIndex < 100; addressIndex += 1) { | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// ... | ||
} | ||
``` | ||
## Seed, xprv, WIFs, & Addrs (With Password) | ||
### Wallet (depth 0) | ||
If the mnemonic is used with the "password" (or "secret") `supersecret123`, it | ||
will produce this HD Wallet seed: | ||
A root HD Key with a `deriveAccount(accountIndex)` method. | ||
Secret: `supersecret123` | ||
Can also derive BIP-32 HD Keys. | ||
```txt | ||
6f9c7acc33e3690d734de56619936d7dce1d3aadf624cdbb09e50a3c978c13234f59112e791910d0cd94c483113dcab0a637cb7f7b85fa78e7af6464e3967713 | ||
From a Seed: | ||
```js | ||
let words = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; | ||
let secret = "TREZOR"; | ||
let seed = await DashPhrase.toSeed(words, secret); | ||
let wallet = await DashHd.fromSeed(seed, {}); | ||
``` | ||
The `xprv` and `xpub` for the HD prefixes `m/44'/5'/0'/0` and `m/44'/5'/2'/1` | ||
are: | ||
To a different Coin Type or non-BIP-44 scheme: | ||
```sh | ||
# m/44'/5'/0'/0 | ||
xprvA1WUDWFxdE5UGW1XNTnkZnd3K6bdidZBTtzvtEQziBpS3N8tajC4QKyRLmas7DK4HXK76wSXgMV1uV6RbKyM5f4uu1VmguEhAqvzQwr2mrC | ||
```js | ||
let purpose = 44; | ||
let coinType = 0; | ||
let accountIndex = 0; | ||
let account = await DashHd.derivePath( | ||
wallet, | ||
`m/${purpose}'/${coinType}'/${accountIndex}'`, | ||
); | ||
``` | ||
xpub6EVpd1nrTbdmUz5zUVKkvvZms8S886H2q7vXgcpcGXMQvAU38GWJx8HuC28Bm8Cizq7dHJvL6armkvL7vvxRpxUxAmpVQF6s8aq5BRBCMrD | ||
### Account (depth 3) | ||
A hardened HD Key with a `deriveXKey(use)` method. | ||
From a Wallet: | ||
```js | ||
let accountIndex = 0; | ||
let account = wallet.deriveAccount(accountIndex); | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
``` | ||
```sh | ||
# m/44'/5'/2'/1 | ||
xprvA1UUAiR2AFvujb6WyC4TSokLBR48hKSqczH9MZfBXoBUztPnsvWByTuVs5GvmbySGjZ95mZtBMv3p3eBPJFFi4efT8azWz8v5zqVT2dFm6Z | ||
From an HD Path: | ||
xpub6ETpaDwuzdVCx5Az5DbTowh4jStd6nAgzDCk9x4o68iTsgiwRTpSXGDyiNDf9dSC75MLM6wTmfcUntPgNFYZ728zZ84Wb3xs43C8YrGZoap | ||
```js | ||
let accountIndex = 0; | ||
let account = await DashHd.derivePath(wallet, `m/44'/5'/${accountIndex}'`); | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
``` | ||
Given the HD paths `m/44'/5'/0'/0/0` and `m/44'/5'/2'/1/2`, that seed will | ||
produce these WIFs (Private Keys) and Pay Addresses (PubKey Hashes). | ||
### XKey (depth 4) | ||
An non-hardened HD Key with a `deriveAddress(addressIndex)` method. | ||
Represents a non-hardened XPrv or XPub. | ||
Share an XPub with a contact so that they can derive additional addresses to pay | ||
you repeatedly without sacrificing privacy, and without needing interaction from | ||
you to creat ea new address each time. | ||
Share an XPrv with an application so that you can load the XPub side and it can | ||
use the XPrv side to make payments on your behalf. | ||
From an Account: | ||
```js | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// { ..., | ||
// privateKey: privBytes, publicKey: pubBytes } | ||
``` | ||
From an `xprv`: | ||
```js | ||
let xkey = await account.fromXKey(xprv); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// { ..., | ||
// privateKey: privBytes, publicKey: pubBytes } | ||
``` | ||
From an `xpub`: | ||
```js | ||
let xkey = await account.fromXKey(xprv); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// { ..., | ||
// privateKey: null, publicKey: pubBytes } | ||
``` | ||
### Key (depth 5) | ||
The base _HD Key_ type, but with no additional methods. | ||
A fully-derived BIP-44 Address (non-hardened). | ||
```json5 | ||
{ ..., | ||
privateKey: privBytes, publicKey: pubBytes } | ||
``` | ||
From a Wallet: | ||
```js | ||
let accountIndex = 0; | ||
let account = await wallet.deriveAccount(accountIndex); | ||
let use = DashHd.RECEIVE; | ||
let xkey = await account.deriveXKey(use); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
``` | ||
From an XPrv: | ||
```js | ||
let xkey = await account.fromXKey(xprv); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// { ..., | ||
// privateKey: privBytes, publicKey: pubBytes } | ||
``` | ||
From an XPub: | ||
```js | ||
let xkey = await account.fromXKey(xpub); | ||
let addressIndex = 0; | ||
let key = await xkey.deriveAddress(addressIndex); | ||
// { ..., | ||
// privateKey: null, publicKey: pubBytes } | ||
``` | ||
### HDKey | ||
A generic HD Key at any depth. | ||
```json5 | ||
{ | ||
versions: DashHd.MAINNET, | ||
depth: 0, | ||
parentFingerprint: 0, // should be set if depth is non-zero | ||
index: 0, | ||
chainCode: chainBytes, // required | ||
privateKey: privOrNull, // required (or null) | ||
publicKey: pubBytes, // required | ||
} | ||
``` | ||
The same structure as [Key](#key), but documented separately - because BIP-32 | ||
doesn't define semantic differences between HD Keys at different depths (other | ||
than the root). | ||
# Walkthrough | ||
This will focus on the **most typical** use case, where you intend to generate | ||
**multiple addresses** as part of your application's lifecycle. | ||
## Part 1: Passphrase, Secret, Seed | ||
The Passphrase (or Seed), **is** the Wallet. | ||
- A _Wallet_ is derived from a _Seed_. | ||
- A _Seed_ is typically derived from a _Passphrase Mnemonic_ and _Secret Salt_. | ||
From a code perspective: | ||
1. If your user doesn't supply a _Passphrase Mnemonic_, you can generate one: | ||
```js | ||
let targetBitEntropy = 128; | ||
let wordList = await DashPhrase.generate(targetBitEntropy); | ||
// "cat swing flag economy stadium alone churn speed unique patch report train" | ||
``` | ||
2. Typically the _Secret Salt_ is left as an **empty string**: | ||
```js | ||
let secretSalt = ""; | ||
``` | ||
3. HOWEVER, for this demo we'll use the _Zoomonic_ and _Zecret_ \ | ||
(these values are specified by BIP-39's test suite for demos, debugging, etc) | ||
```js | ||
let wordList = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"; | ||
let secretSalt = "TREZOR"; | ||
``` | ||
4. _Key Expansion_ derives the _Seed_ from the _Passphrase_ + _Secret_ \ | ||
(it's a specific configuration of PBKDF2 under the hood) | ||
```js | ||
let seedBytes = await DashPhrase.toSeed(wordList, secretSalt); | ||
``` | ||
Prompt the user **to make a backup** of their _Passphrase_. \ | ||
(or their _Seed_, if you're not implementing *Passphrase*s) | ||
It's common to print this out and put it in a safe. | ||
If the **_Passphrase_ is lost**, the Wallet (and all money) is | ||
**unrecoverable**. | ||
## Part 2: The Wallet Derivation | ||
As mentioned in the API section, it is, in fact, possible for any derivation to | ||
fail. | ||
It's highly unlikely that you'll ever encounter it, but it should be handled | ||
nonetheless. | ||
1. Generate a _Wallet_ Key \ | ||
(uses an HMAC-style Key Expansion, defined in BIP-32 ) | ||
```js | ||
let wallet; | ||
try { | ||
wallet = await DashHd.fromSeed(seed); | ||
} catch (e) { | ||
window.alert("the passphrass (or seed) could not be used to derive keys"); | ||
} | ||
``` | ||
2. Notify the user and retry a different Passphrase on failure. | ||
### Part 2a: HD Path Derivation | ||
As a **one-off**, HD Path Derivation can be very convenient: | ||
Note: this approach would **5x slower** for deriving multiple keys because each | ||
key will derive from the Root Wallet Key each time. | ||
1. Define the target HD Path indexes to Depth 4 (_Use_ / _X Key_) | ||
```js | ||
let accountIndex = 0; | ||
let use = DashHd.RECEIVE; | ||
let addressIndex = 0; | ||
let maxTries = 3; | ||
let hdPartial = `m/44'/5'/${accountIndex}'/${use}`; | ||
``` | ||
2. Derive the Address Key (Depth 5) | ||
```js | ||
let key; | ||
for (let i = addressIndex; i < maxTries; i += 1) { | ||
try { | ||
let hdpath = `${hdPartial}/${addressIndex}`; | ||
key = DashHd.derivePath(wallet, hdpath); // as defined above | ||
break; | ||
} catch (e) { | ||
// ignore | ||
} | ||
} | ||
if (!key) { | ||
// more than 1 failure in a row would indicate | ||
// the accountIndex or use index could not be derived | ||
// (and hence no addressIndex will ever derive) | ||
throw new Error( | ||
`'${hdPartial}': 'account' or 'use' index cannot be derived`, | ||
); | ||
} | ||
``` | ||
3. Mark the Account index as invalid and advance to the next on failure. \ | ||
(or fail hard if it's the first account) | ||
### Part 2b: _Wallet_, _Account_, _X Key_ | ||
This is the more **typical** and **efficient** use - for when you intend to | ||
generate **multiple addresses** as part of your application's lifecycle. | ||
1. Derive an Account Key and X Key \ | ||
(reject the seed if the account at index 0 fails) | ||
```js | ||
let accountIndex = 0; | ||
let account; | ||
let use = DashHd.RECEIVE; // 0 = receive, 1 = change | ||
let xkey; | ||
while (!account) { | ||
try { | ||
account = await wallet.deriveAccount(accountIndex); | ||
xkey = await account.deriveXKey(use); | ||
break; | ||
} catch (e) { | ||
accountIndex += 1; | ||
// if your app handles multiple accounts, just try the next | ||
} | ||
} | ||
``` | ||
Note: technically you could advance the Use index, \ | ||
but that's more complicated than just advancing to the next account | ||
2. (optional) Encode the _X Key_ as XPrv or XPub for sharing | ||
```js | ||
let xprv = await DashHd.toXPrv(xkey); // "xprv......" | ||
let xpub = await DashHd.toXPub(xkey); // "xpub......" | ||
``` | ||
## Part 3: _Address Key_ | ||
This is final piece, which you use for making and receiving payments. | ||
1. Derive an Address Key | ||
```js | ||
let index = 0; | ||
let maxTries = 3; | ||
let last = index + maxTries; | ||
let key; | ||
for (let i = index; i < last; i += 1) { | ||
try { | ||
key = await xkey.deriveAddress(index); | ||
} catch (e) { | ||
// you may wish to mark the index as failed | ||
} | ||
} | ||
``` | ||
2. Encode the Address Key as WIF or Address for use or sharing | ||
```js | ||
let wif = DashKeys.toWif(keys.privateKey); // "X....." | ||
let addr = DashKeys.toAddr(keys.publicKey); // "X..." | ||
``` | ||
# Glossary | ||
See also [Dash Tools Glossary](https://github.com/dashhive/dash-tools#glossary). | ||
- [Base2048](#base2048) | ||
- [Base58Check](#base58Check) | ||
- [BIPs](#bip) | ||
- [BIP-32](#bip-32) | ||
- [BIP-39](#bip-39) | ||
- [BIP-43](#bip-43) | ||
- [BIP-44](#bip-44) | ||
- [Curve](#curve) | ||
- [Derive (by Path)](#derive-by-path) | ||
- [Derived Child Key](#derived-child) | ||
- [Key Expansion](#key-expansion) | ||
- [HD Account](#hd-account) | ||
- [HD Address Key](#hd-address-key) | ||
- [HD Keys](#hd-keys) | ||
- [HD Passphrase Mnemonic](#hd-passphrase-mnemonic) | ||
- [HD Path](#hd-path) | ||
- [HD Wallet](#hd-wallet) | ||
- [HD X Key](#hd-x-key) | ||
- [Root Seed](#root-seed) | ||
- [Root Key](#root-key) | ||
- [Secp256k1](#secp256k1) | ||
- [Test Vectors](#test-vectors) | ||
- [XPrv](#xprv) | ||
- [XPub](#xpub) | ||
- [Zecret](#zecret) | ||
- [Zeed](#zeed) | ||
- [Zoomonic](#zoomonic) | ||
## Base2048 | ||
Also: Base2048, _BIP39_, _BIP-0039_ | ||
Rather than a bank of 2, 16, 32, 58, 62, or 64 characters, \ | ||
you can encode data using a bank of whole words. \ | ||
If you use 2048 words, each word represents 11 _bits_. \ | ||
12 words represent 131 _bits_ of information. \ | ||
Any extra bits are used for checksumming the data. \ | ||
See [_HD Passphrase Mnemonic_](#hd-passphrase-mnemonic). | ||
## Base58Check | ||
The encoding format used for sharing _XPrv_ and _XPub_ Keys (_X Keys_). \ | ||
(among other things, such as _WIF_ and _Address_) | ||
```text | ||
xprvA2L7qar7dyJNhxnE47gK5J6cc1oEHQuAk8WrZLnLeHTtnkeyP4w6Eo6Tt65trtdkTRtx8opazGnLbpWrkhzNaL6ZsgG3sQmc2yS8AxoMjfZ | ||
``` | ||
```text | ||
xpub6FKUF6P1ULrfvSrhA9DKSS3MA3digsd27MSTMjBxCczsfYz7vcFLnbQwjP9CsAfEJsnD4UwtbU43iZaibv4vnzQNZmQAVcufN4r3pva8kTz | ||
``` | ||
## BIP | ||
Also: BIPs | ||
Bitcoin Improvement Proposal(s). \ | ||
Specification Drafts / RFCs (Request For Comments). | ||
## BIP-32 | ||
See [_HD Keys_](#hd-keys). | ||
## BIP-39 | ||
Also: Base2048, _BIP39_, _BIP-0039_ | ||
BIP for [_HD Passphrase Mnemonic_](#hd-passphrase-mnemonic). | ||
## BIP-43 | ||
BIP for the _Purpose_ index of the _HD Path_. | ||
```js | ||
`m/${purpose}'`; | ||
``` | ||
This is the basis of [BIP-44][#bip-44] defining HD Paths as `m/44'/`. | ||
See [_HD Keys_](#hd-keys). | ||
## BIP-44 | ||
See [_HD Path_](#hd-path). | ||
## Curve | ||
Related to parameters of Elliptic Curve (ECDSA) cryptography / algorithms. | ||
A single X value produces two Y values on a curve (rather than 1 on a line). | ||
In rare instances, an X value may produce **no points** on the curve. | ||
## Derive (by Path) | ||
To split an HD Path by `/` and then iterate to derive each index (_Child_) in | ||
turn. | ||
**Cannot be reversed**. | ||
See [`derivePath(hdkey, hdpath)`][#derivePath-hdkey-hdpath]. | ||
## Derived Child | ||
A key directly derived from another key by an _HD Path_ index. \ | ||
(typically referring to a single index of the path, not the whole) | ||
See | ||
[`deriveChild(hdkey, index, isHardened)`][#derivePath-hdkey-index-ishardened]. | ||
## Key Expansion | ||
An algorithm that creates a larger (byte-size) output than its input. \ | ||
Typically uses hashing algos: HMAC, SHA-2, SHA-3, etc. \ | ||
May combine multiple algos together. \ | ||
Usually intentionally slow. \ | ||
May run a high number of "Rounds" in succession. \ | ||
(typically hundreds or thousands). | ||
_Passhrase Mnemonics_ to _Seed_ (BIP-39) uses _PBKDF2_. \ | ||
_HD Keys_ (BIP-44) use HMAC and *Secp256k1 Tweak*ing for each index. | ||
See also: | ||
- `DashPhrase.toSeed(wordList)` | ||
- `DashHd.fromSeed(seedBytes)` | ||
- `DashHd.deriveChild(hdkey, index, isHardened)` | ||
## HD Account | ||
An HD Key derived at `m/44'/5'/n'` (Depth 3) of the HD Path. | ||
See [API: Key Types](#key-types). | ||
## HD Address Key | ||
Also: _Key_, _HD Private Key_, _WIF_, _Address_ | ||
An HD Key at final depth `m/44'/5'/0'/0/0` (Depth 5) of an _HD Path_. \ | ||
Can be encoded as _WIF_ or _Address_ for making or receiving payments. | ||
See also [API: Key Types](#key-types). | ||
## HD Keys | ||
Also: _Hierarchical Deterministic Keys_, _BIP-32_, _BIP-44_ | ||
Any of generic or purpose-specific keys derived deterministically form a seed. | ||
See more at [API: Key Types](#key-types) (code above) and [HD Path](#hd-path). | ||
## HD Passphrase Mnemonic | ||
Also: _Mnemonic for Generating Hierarchical Deterministic Keys_, _HD Wallet_, | ||
_BIP-39_ | ||
12 words used to derive an HD Seed. \ | ||
(11¾ for entropy, ¼ for checksum) | ||
Ex: `zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong` | ||
Not used _directly_ in this library, but...\ | ||
it _is_ how the HD Seeds used here are typically generated. | ||
See [DashPhrase.js](dash-phrase). | ||
## HD Path | ||
The path that defines an HD Key - typically of the BIP-44 variety: | ||
- a _Root_, ex: `m` (depth 0, the wallet, straight from the seed) | ||
- an _Coin Key_, ex: `m/44'/5'` (depth 2, sometimes called Wallet) | ||
- an _Account_, ex: `m/44'/5'/0'` (depth 3) | ||
- an _X Key_, ex: `m/44'/5'/0'/0` (depth 4, also the Use) | ||
- an _Address Key_, ex: `m/44'/5'/0'/0/0` (depth 5, the end) | ||
In general: | ||
```js | ||
let hdpath = `m/${purpose}'/${coinType}'/${account}'/${use}/${index}`; | ||
``` | ||
For DASH: | ||
```js | ||
let hdpath = `m/44'/5'/${account}'/${use}/${index}`; | ||
``` | ||
See also [API: Key Types](#key-types) (code above). | ||
## HD Wallet | ||
Either the _Root Key_ at `m` (Depth 0), directly from the _Seed_, \ | ||
or the _Coin Key_ at `m/44'/5'` (Depth 2), of the _HD Path_. \ | ||
Sometimes also used to mean _HD Account_ at `m/44'/5'/n'`. | ||
Here we typically use it to mean the _Root Key_. \ | ||
(because we're focus on DASH more so than other coins) | ||
See also [API: Key Types](#key-types). | ||
## HD X Key | ||
Also: _XKey_, _XPrv_, _XPub_, _Use Key_, _Use Index_, _Extended Key_. | ||
An HD Key derived at `m/44'/5'/0'/n` (Depth 4), of the _HD Path_. | ||
Here we typically use it to mean the _Root Key_. \ | ||
(because we're focus on DASH more so than other coins) | ||
See also [API: Key Types](#key-types). | ||
## Root Seed | ||
Also: Master Seed, Seed, HD Seed | ||
Either: | ||
- 64 random bytes | ||
- a 64-byte hash derived from a _Passphrase Mnemonic_ | ||
**Cannot be reversed**. | ||
## Root Key | ||
Also: _HD Wallet_, _Master Key_, _HD Master_ | ||
An HD Key of `m` (Depth 0), as derived directly from the Seed. | ||
See also [API: Key Types](#key-types). | ||
## Secp256k1 | ||
A specific set of parameters "the curve" used by most cryptocurrencies. | ||
See [Curve](#curve). | ||
## Test Vectors | ||
The well-known values used for testing, demos, debugging, and development: | ||
- DashPhrase / BIP-39: | ||
- <https://github.com/trezor/python-mnemonic/blob/master/vectors.json> | ||
- Includes the [_Zoomonic_](#zoomonic), [_Zecret_](#zecret), and | ||
[_Zeed_](#zeed). | ||
- Generic HD Key / BIP-32: | ||
- <https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors> | ||
- DashKeys / BIP-44: | ||
- [Zoomonic](#zoomonic) | ||
## XPrv | ||
Also: _Extended Private Key_, _XPriv_, _X Prv_, _X Priv_ | ||
Specifically the Base58Check-encoded form of an HD Key at Depth 4. \ | ||
(the _X Key_, a.k.a. _Use Key_, including the _Private Key_)\_ \ | ||
Can be used to derive any number of *WIF*s and *Address*es. | ||
```text | ||
xprvA2L7qar7dyJNhxnE47gK5J6cc1oEHQuAk8WrZLnLeHTtnkeyP4w6Eo6Tt65trtdkTRtx8opazGnLbpWrkhzNaL6ZsgG3sQmc2yS8AxoMjfZ | ||
``` | ||
See [HD X Key](#hd-x-key). | ||
## XPub | ||
Also: _Extended Pubilc Key_, _X Pub_ | ||
Specifically the Base58Check-encoded form of an HD Key. \ | ||
(just the public key) Can be used to derive any number of receiving *Address*es. | ||
```text | ||
xpub6FKUF6P1ULrfvSrhA9DKSS3MA3digsd27MSTMjBxCczsfYz7vcFLnbQwjP9CsAfEJsnD4UwtbU43iZaibv4vnzQNZmQAVcufN4r3pva8kTz | ||
``` | ||
See [XPrv](#xprv), [HD X Key](#hd-x-key). | ||
## Zecret | ||
The _Secret Salt_ used for the BIP-32 Test Vectors. | ||
```text | ||
TREZOR | ||
``` | ||
```js | ||
let secretSalt = "TREZOR"; | ||
``` | ||
Comes from the fact that the company Trezor (a hardware wallet) was involved in | ||
creating the reference implementation and Test Vectors. | ||
## Zeed | ||
The canonical Seed (generated from the Zoomonic salted with "TREZOR"), \ | ||
to be used in documentation, examples, and test fixtures. | ||
```txt | ||
(account 0, external/receiving, address 0) | ||
m/44'/5'/0'/0/0: XKHiWYkmDkNnWGP756UCGcuZ21mHGeYdWeCBBHCBGZaf3NYw1SAz (WIF) | ||
XjxyR1gve94LuKqkMLEeqJbEVM5B5q1ZSx (Addr) | ||
m/44'/5'/0'/0/1: XCsy8Qw1fLH7C1UxLjBfTfLpn8DMRK1TMNNE2a5J1F4TyE5UApcK | ||
XxRrwh1xBWig9rfLyiy494u2vj6YXQMsH7 | ||
ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069 | ||
``` | ||
(account 2, internal/change, address 2) | ||
m/44'/5'/2'/1/2: XBwqVpx9SLtvoscmLgC2AtXoKZi5FxYKtYbPGTyjzsKBxsfAxrmy (WIF) | ||
XhWFxtNSqwTqLYAQ9XQJbfQG3Hj64qLoGt (Addr) | ||
## Zoomonic | ||
```txt | ||
Passphrase (Mnemonic) : zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong | ||
Secret (Salt Password) : TREZOR | ||
Seed : ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069 | ||
``` | ||
**Misnomer Alert**: The so-called "secret" is actually a pbkdf2 _salt_, yet it's | ||
sometimes also referred to as a "passphrase" or "password"... oh well 🤷♂️. | ||
# References | ||
## More Fixtures | ||
- https://github.com/dashhive/dash-tools | ||
- https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/hdnode.js | ||
- http://bip32.org/ | ||
- http://blog.richardkiss.com/?p=313 | ||
- https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki | ||
- http://bitcoinmagazine.com/8396/deterministic-wallets-advantages-flaw/ | ||
See [FIXTURES.md](./FIXTURES.md). | ||
# License | ||
Copyright © 2023 Dash Incubator \ | ||
Copyright © 2023 AJ ONeal \ | ||
Copyright © 2018-2022 cryptocoinjs | ||
MIT License |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
GitHub dependency
Supply chain riskContains a dependency which resolves to a GitHub URL. Dependencies fetched from GitHub specifiers are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
70786
1
1299
0
0
4
5
871
2
1
+ Addeddashkeys@^1.0.0
+ Addeddashkeys@1.1.5(transitive)
- Removed@dashincubator/base58check@^1.3.1
- Removed@dashincubator/ripemd160@^2.3.0
- Removedbip39@^3.0.4
- Removedhdkey@github:dashhive/hdkey
- Removedqrcode-svg@^1.1.0
- Removed@dashincubator/base58check@1.4.1(transitive)
- Removed@dashincubator/ripemd160@2.3.0(transitive)
- Removed@noble/hashes@1.5.0(transitive)
- Removedbip39@3.1.0(transitive)
- Removedqrcode-svg@1.1.0(transitive)