-
Notifications
You must be signed in to change notification settings - Fork 24
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
feat(hd-wallet): add package #1151
Changes from 9 commits
3ff55db
2cddfea
030a30a
fef63b4
00b91c4
9c943df
90fec0f
7e484e6
b77929b
ea723e0
2583896
bb5c7f4
4f0f158
7b02cd8
ab0d912
e09bd42
175098c
629d0b4
a449c90
ffea630
b0fd9e7
e041095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
src/chainweaver/vendor/**/* |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// This is a workaround for https://github.com/eslint/eslint/issues/3458 | ||
require('@rushstack/eslint-config/patch/modern-module-resolution'); | ||
|
||
module.exports = { | ||
extends: ['@kadena-dev/eslint-config/profile/lib'], | ||
parserOptions: { tsconfigRootDir: __dirname }, | ||
rules: { | ||
'@typescript-eslint/explicit-function-return-type': 'off', | ||
'@rushstack/typedef-var': 'off', | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
etc | ||
lib | ||
temp | ||
src/chainweaver/vendor/**/* | ||
**/*.md | ||
tsdoc-metadata.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<!-- genericHeader start --> | ||
|
||
# @kadena/hd-wallet | ||
|
||
The library for managing seed generation and key derivation for hd wallets. | ||
Chainweaver protocol: supports legacy chainweaver bip39/bip32 with a custom | ||
derivation path | ||
|
||
## BIP39 | ||
|
||
Seed generation based on bip39 and 12 words | ||
|
||
## Chainweaver protocol: | ||
|
||
it uses the bundle file from | ||
[kadena-crypto](https://github.com/kadena-io/cardano-crypto.js/tree/jam%40chainweaver-keygen) | ||
repo | ||
|
||
## BIP44: | ||
|
||
derivation path: `m/44'/626'/${index}'/0'/0'`; all keys hardened | ||
|
||
## Public Key | ||
|
||
public key generation algorithm: `ed25519` | ||
|
||
<picture> | ||
<source srcset="https://raw.githubusercontent.com/kadena-community/kadena.js/main/common/images/Kadena.JS_logo-white.png" media="(prefers-color-scheme: dark)"/> | ||
<img src="https://raw.githubusercontent.com/kadena-community/kadena.js/main/common/images/Kadena.JS_logo-black.png" width="200" alt="kadena.js logo" /> | ||
</picture> | ||
|
||
<!-- genericHeader end --> | ||
|
||
### Architectural decisions | ||
|
||
Check [ADRs](./docs/decisions/) documents for more information |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Use BIP44 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe somewhere in this decision record, you could also mention something about not supporting |
||
|
||
Date: 2023-10-31 | ||
|
||
Status: accepted | ||
|
||
## Context | ||
|
||
In the context of HD (Hierarchical Deterministic) wallets, keys are generated | ||
using the BIP32 algorithm. BIP32 allows for various approaches in defining | ||
derivation paths, as it accepts a derivation path to create branched key trees. | ||
|
||
## Decision | ||
|
||
We have opted to utilize the [BIP44][1] protocol for our HD wallet | ||
implementation. This decision is based on the following considerations: | ||
|
||
- KDA (Kadena) uses coin-type [626][2]. | ||
- KDA is an account-model coin, and for each key, we change the account (third | ||
item in BIP44). | ||
- All keys are hardened due to the [ed25519][3] public key generation algorithm, | ||
which is enforced by Kadena, necessitating hardened key restrictions. | ||
|
||
As a result, the derivation path for our HD wallet will be as follows: | ||
|
||
``` | ||
m/44'/626'/${index}'/0'/0' | ||
``` | ||
|
||
## Consequences | ||
|
||
- We use a standard approach for key derivation which would be more compatible | ||
with other blockchians | ||
- finding library for implementation would be easier | ||
|
||
- We have to managed legacy algorithm like chainweaver for backward | ||
compatibility | ||
|
||
- Some wallets already use `m/44'/626'/0'/0'/${index}'` instead of the adopted | ||
approach. So we need to support this path as well | ||
|
||
- Since Kadena using ed25519 (only hardened keys) we can not use extended keys | ||
mentioned in the BIP44 protocol | ||
|
||
## Resources | ||
|
||
- [BIP44 proposal][1] | ||
- [SLIP-0044 : Registered coin types for BIP-0044][2] | ||
- [SLIP-0010 : Universal private key derivation from master private key][3] | ||
|
||
[1]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki | ||
[2]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md | ||
[3]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Supporting Chainweaver Key Derivation | ||
|
||
Date: 2023-10-31 | ||
|
||
Status: Accepted | ||
|
||
## Context | ||
|
||
We need to support chainweaver users. Discussions have revolved around | ||
supporting keypair import or key derivation from a seed. | ||
|
||
## Decision | ||
|
||
We have chosen to use the bundle file from the [kadena-io/cardano-crypto][1] | ||
repository. This choice is driven by the following factors: | ||
|
||
1. **Custom Algorithm**: The bundle file contains a custom key derivation | ||
algorithm based on BIP32. that we cant find an alternative library for that. | ||
|
||
2. **Lack of Documentation**: The library lacks comprehensive documentation, | ||
making maintenance and understanding of its design challenging. To mitigate | ||
potential issues stemming from this lack of documentation, we have opted to | ||
use the bundle as-is. | ||
|
||
3. **Dependency Considerations**: The library relies on a C library through | ||
WebAssembly (WASM), introducing specific dependencies during the build | ||
process. To maintain a streamlined monorepo, we have decided against | ||
including these dependencies. | ||
|
||
## Consequences | ||
|
||
This decision has the following implications: | ||
|
||
- The bundle file must be included in the git repository. | ||
- We may need to find ways to let users use the library for BIP44 without | ||
requiring its inclusion in output files. | ||
|
||
## Resources | ||
|
||
[kadena-io/cardano-crypto repository][1] | ||
|
||
[1]: | ||
https://github.com/kadena-io/cardano-crypto.js/tree/jam%40chainweaver-keygen |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,66 @@ | ||||||
{ | ||||||
"name": "@kadena/hd-wallet", | ||||||
"version": "0.0.1", | ||||||
"private": true, | ||||||
"description": "HD Wallet; key derivation", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
"repository": { | ||||||
"type": "git", | ||||||
"url": "https://github.com/kadena-community/kadena.js.git", | ||||||
"directory": "packages/libs/hd-wallet" | ||||||
}, | ||||||
"license": "MIT", | ||||||
"contributors": ["Javad Khalilian <javad@kadena.io>"], | ||||||
"exports": { | ||||||
"./chainweaver": "./lib/chainweaver/index.js" | ||||||
}, | ||||||
"types": "./lib/index.d.ts", | ||||||
"main": "./lib/index.js", | ||||||
"typesVersions": { | ||||||
"*": { | ||||||
"chainweaver": [ | ||||||
"./lib/chainweaver/index.d.ts" | ||||||
] | ||||||
} | ||||||
}, | ||||||
"files": [ | ||||||
"lib" | ||||||
], | ||||||
"scripts": { | ||||||
"build": "tsc && cpy ./src/chainweaver/vendor/**/* ./lib/chainweaver/vendor/", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add api-extractor to this package? |
||||||
"format": "pnpm run --sequential /^format:.*/", | ||||||
"format:lint": "pnpm run lint:src --fix", | ||||||
"format:md": "remark *.md -o --use @kadena-dev/markdown", | ||||||
"format:src": "prettier . --cache --write", | ||||||
"lint": "pnpm run /^lint:.*/", | ||||||
"lint:fmt": "prettier . --cache --check", | ||||||
"lint:pkg": "lint-package", | ||||||
"lint:src": "eslint src --ext .js,.ts", | ||||||
"start": "ts-node --transpile-only src/index.ts", | ||||||
"test": "vitest" | ||||||
}, | ||||||
"dependencies": { | ||||||
"@kadena/cryptography-utils": "workspace:*", | ||||||
"debug": "~4.3.4" | ||||||
}, | ||||||
"devDependencies": { | ||||||
"@kadena-dev/eslint-config": "workspace:*", | ||||||
"@kadena-dev/lint-package": "workspace:*", | ||||||
"@kadena-dev/markdown": "workspace:*", | ||||||
"@kadena-dev/shared-config": "workspace:*", | ||||||
"@kadena/types": "workspace:*", | ||||||
"@microsoft/api-extractor": "^7.38.0", | ||||||
"@rushstack/eslint-config": "~3.3.0", | ||||||
"@types/debug": "~4.1.7", | ||||||
"@types/node": "^18.17.14", | ||||||
"@vitest/coverage-v8": "^0.34.6", | ||||||
"cpy-cli": "^5.0.0", | ||||||
"eslint": "^8.45.0", | ||||||
"prettier": "~3.0.3", | ||||||
"ts-node": "~10.8.2", | ||||||
"typescript": "5.2.2", | ||||||
"vitest": "^0.34.6" | ||||||
}, | ||||||
"publishConfig": { | ||||||
"provenance": true | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export { | ||
kadenaChangePassword, | ||
kadenaCheckMnemonic, | ||
kadenaGenKeypair, | ||
kadenaGenMnemonic, | ||
kadenaGetPublic, | ||
kadenaMnemonicToRootKeypair, | ||
kadenaSign, | ||
kadenaVerify, | ||
} from './vendor/kadena-crypto'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { describe, expect, it } from 'vitest'; | ||
import { | ||
kadenaGenKeypair, | ||
kadenaGenMnemonic, | ||
kadenaMnemonicToRootKeypair, | ||
} from '..'; | ||
import { harden } from '../../utils'; | ||
|
||
const toHexStr = (bytes: Uint8Array) => Buffer.from(bytes).toString('hex'); | ||
|
||
describe('kadenaGenMnemonic', () => { | ||
it('should generate a mnemonic', () => { | ||
const mnemonic = kadenaGenMnemonic(); | ||
const wordsList = mnemonic.split(' '); | ||
expect(wordsList).toHaveLength(12); | ||
expect(wordsList.every((word) => word.length > 2)).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('kadenaMnemonicToRootKeypair', () => { | ||
it('should generate a Root key from mnemonic', () => { | ||
const mnemonic = | ||
'bubble fade wasp analyst then panel desert hold spatial sound lucky weekend'; | ||
const password = 'password'; | ||
const rootKey = kadenaMnemonicToRootKeypair(password, mnemonic); | ||
expect(rootKey).toHaveLength(128); | ||
}); | ||
|
||
it('should generate a the same root key from the same mnemonic', () => { | ||
const mnemonic = | ||
'bubble fade wasp analyst then panel desert hold spatial sound lucky weekend'; | ||
const password = 'password'; | ||
const rootKey1 = kadenaMnemonicToRootKeypair(password, mnemonic); | ||
const rootKey2 = kadenaMnemonicToRootKeypair(password, mnemonic); | ||
|
||
expect(toHexStr(rootKey1)).toBe(toHexStr(rootKey2)); | ||
}); | ||
|
||
it('should generate different root key for different mnemonic', () => { | ||
const mnemonicFirst = | ||
'bubble fade wasp analyst then panel desert hold spatial sound lucky weekend'; | ||
|
||
const mnemonicSecond = | ||
'tuna nerve predict all catch early oblige inform hamster magnet century goddess'; | ||
const password = 'password'; | ||
const rootKey1 = kadenaMnemonicToRootKeypair(password, mnemonicFirst); | ||
const rootKey2 = kadenaMnemonicToRootKeypair(password, mnemonicSecond); | ||
|
||
expect(toHexStr(rootKey1)).not.toBe(toHexStr(rootKey2)); | ||
}); | ||
|
||
it('should generate different root key for different passwords same mnemonic', () => { | ||
const mnemonic = | ||
'bubble fade wasp analyst then panel desert hold spatial sound lucky weekend'; | ||
const rootKey1 = kadenaMnemonicToRootKeypair('pass-one', mnemonic); | ||
const rootKey2 = kadenaMnemonicToRootKeypair('pass-two', mnemonic); | ||
|
||
expect(toHexStr(rootKey1)).not.toBe(toHexStr(rootKey2)); | ||
}); | ||
}); | ||
|
||
describe('kadenaGenKeypair', () => { | ||
it('should generate keypair frpm the rootKey', () => { | ||
const mnemonic: string = | ||
'bubble fade wasp analyst then panel desert hold spatial sound lucky weekend'; | ||
const password = 'password'; | ||
const rootKey = kadenaMnemonicToRootKeypair(password, mnemonic); | ||
const [encryptedSecret, publicKey] = kadenaGenKeypair( | ||
password, | ||
rootKey, | ||
harden(1), | ||
); | ||
expect(encryptedSecret.byteLength).toEqual(128); | ||
expect(publicKey.byteLength).toBe(32); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,36 @@ | ||||||||||||||||||||||||
export declare const kadenaChangePassword: ( | ||||||||||||||||||||||||
privateKey: string | Uint8Array, | ||||||||||||||||||||||||
oldPassword: string, | ||||||||||||||||||||||||
newPassword: string, | ||||||||||||||||||||||||
) => Uint8Array; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaCheckMnemonic: (mnemonic: string) => boolean; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaGenKeypair: ( | ||||||||||||||||||||||||
password: string, | ||||||||||||||||||||||||
rootKey: string | Uint8Array, | ||||||||||||||||||||||||
index: number, | ||||||||||||||||||||||||
) => [Uint8Array, Uint8Array]; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaGenMnemonic: () => string; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaGetPublic: (secretKey: Uint8Array) => Uint8Array; | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function should probably accept the |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaMnemonicToRootKeypair: ( | ||||||||||||||||||||||||
password: string, | ||||||||||||||||||||||||
mnemonic: string, | ||||||||||||||||||||||||
) => Uint8Array; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaSign: ( | ||||||||||||||||||||||||
password: string, | ||||||||||||||||||||||||
message: string, | ||||||||||||||||||||||||
privateKey: string, | ||||||||||||||||||||||||
) => Uint8Array; | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we change this to use the
Suggested change
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
export declare const kadenaVerify: ( | ||||||||||||||||||||||||
message: string, | ||||||||||||||||||||||||
publicKey: string, | ||||||||||||||||||||||||
signature: string, | ||||||||||||||||||||||||
) => boolean; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
// Path: ./kadena-crypto.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this file?