* BIP39/44 private key derivation
* Mnemonic is compatible with the Ledger Nano implementation
* Import wallets with a mnemonic phrase or a seed
+* Import wallets with the legacy Nano hex seed
* Sign send, receive and change representative blocks with a private key
* Runs in all web browsers and mobile frameworks built with Javascript
* Convert Nano units
// Import a wallet with a seed
const wallet = wallet.fromSeed(seed)
+// Import a wallet with a legacy hex seed
+const wallet = wallet.fromLegacySeed(seed)
+
// Derive private keys for a seed, from and to are number indexes
const accounts = wallet.accounts(seed, from, to)
+
+// Derive private keys for a legacy seed, from and to are number indexes
+const accounts = wallet.legacyAccounts(seed, from, to)
```
```javascript
-// The returned wallet JSON format is as follows:
+// The returned wallet JSON format is as follows. The mnemonic phrase will be undefined when importing with a seed.
{
mnemonic: 'edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur',
seed: '0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c',
#### Signing any data with the private key
-For example implementing client side login with the password being the user's e-mail signed with their private key
+For example implementing client side login with the password being the user's e-mail signed with their private key. Make sure that you double check the signature on the back-end side with the public key.
```javascript
import { tools } from 'nanocurrency-web'
### In web
```html
-<script src="https://unpkg.com/nanocurrency-web@1.1.1" type="text/javascript"></script>
+<script src="https://unpkg.com/nanocurrency-web@1.2.0" type="text/javascript"></script>
<script type="text/javascript">
NanocurrencyWeb.wallet.generate(...);
</script>
* An optional seed password can be used to encrypt the mnemonic phrase so the seed
* cannot be derived correctly without the password. Recovering the password is not possible.
*
- * @param {string} [entropy] Optional 64 byte hexadecimal string entropy to be used instead of the default
- * @param {string} [seedPassword] Optional seed password
+ * @param {string} [entropy] - (Optional) 64 byte hexadecimal string entropy to be used instead of the default
+ * @param {string} [seedPassword] - (Optional) seed password
* @returns the generated mnemonic, seed and account
*/
generate: (entropy?: string, seedPassword?: string): Wallet => {
* The Nano address is derived from the public key using standard Nano encoding.
* The address is prefixed with 'nano_'.
*
- * @param {string} mnemonic The mnemonic phrase. Words are separated with a space
- * @param {string} [seedPassword] Optional seed password
+ * @param {string} mnemonic - The mnemonic phrase. Words are separated with a space
+ * @param {string} [seedPassword] - (Optional) seed password
* @throws Throws an error if the mnemonic phrase doesn't pass validations
- * @returns the imported mnemonic, seed and account
+ * @returns the wallet derived from the mnemonic (mnemonic, seed, account)
*/
fromMnemonic: (mnemonic: string, seedPassword?: string): Wallet => {
return importer.fromMnemonic(mnemonic, seedPassword)
* The Nano address is derived from the public key using standard Nano encoding.
* The address is prefixed with 'nano_'.
*
- * @param {string} seed The seed
- * @returns the importes seed and account
+ * @param {string} seed - The seed
+ * @returns {Wallet} the wallet derived from the seed (seed, account)
*/
fromSeed: (seed: string): Wallet => {
return importer.fromSeed(seed)
},
+ /**
+ * Import Nano cryptocurrency accounts from a legacy hex seed
+ *
+ * This function imports a wallet from a seed. The private key is derived from the seed using
+ * simply a blake2b hash function. The public key is derived from the private key using the ed25519 curve
+ * algorithm.
+ *
+ * The Nano address is derived from the public key using standard Nano encoding.
+ * The address is prefixed with 'nano_'.
+ *
+ * @param {string} seed - The seed
+ * @returns the wallet derived from the seed (seed, account)
+ *
+ */
+ fromLegacySeed: (seed: string): Wallet => {
+ return importer.fromLegacySeed(seed);
+ },
+
/**
* Derive accounts for the seed
*
* from the given seed with input parameters 44'/165' and indexes based on the from and to
* parameters.
*
- * @param {string} seed The seed
- * @param {number} from The start index
- * @param {number} to The end index
+ * @param {string} seed - The seed
+ * @param {number} from - The start index
+ * @param {number} to - The end index
*/
accounts: (seed: string, from: number, to: number): Account[] => {
return importer.fromSeed(seed, from, to).accounts
},
+ /**
+ * Derive accounts for the legacy hex seed
+ *
+ * This function derives Nano accounts with the given seed with indexes
+ * based on the from and to parameters.
+ *
+ * @param {string} seed - The seed
+ * @param {number} from - The start index
+ * @param {number} to - The end index
+ */
+ legacyAccounts: (seed: string, from: number, to: number): Account[] => {
+ return importer.fromLegacySeed(seed, from, to).accounts
+ },
+
}
const blockSigner = new BlockSigner()
import Bip39Mnemonic from './bip39-mnemonic'
import Ed25519 from './ed25519'
import NanoAddress from './nano-address'
+import Signer from './signer'
+import Convert from './util/convert'
export default class AddressImporter {
return this.nano(seed, from, to, undefined)
}
+
+
+ /**
+ * Import a wallet using a legacy seed
+ *
+ * @param {string} seed - The seed to import the wallet from
+ * @param {number} [from] - (Optional) The start index of the private keys to derive from
+ * @param {number} [to] - (Optional) The end index of the private keys to derive to
+ * @returns {Wallet} The wallet derived from the seed
+ */
+ fromLegacySeed(seed: string, from: number = 0, to: number = 0): Wallet {
+ const signer = new Signer()
+
+ const accounts: Account[] = []
+ for (let i = from; i <= to; i++) {
+ const privateKey = Convert.ab2hex(signer.generateHash([seed, Convert.dec2hex(i, 4)]))
+
+ const ed25519 = new Ed25519()
+ const keyPair = ed25519.generateKeys(privateKey)
+
+ const nano = new NanoAddress()
+ const address = nano.deriveAddress(keyPair.publicKey)
+
+ accounts.push({
+ accountIndex: i,
+ privateKey: keyPair.privateKey,
+ publicKey: keyPair.publicKey,
+ address,
+ })
+ }
+
+ return {
+ mnemonic: undefined,
+ seed,
+ accounts,
+ }
+ }
/**
* Derives the private keys
const nano = new NanoAddress()
const address = nano.deriveAddress(keyPair.publicKey)
+
accounts.push({
accountIndex: i,
privateKey: keyPair.privateKey,
*
* @param data Data to hash
*/
- private generateHash(data: string[]): Uint8Array {
+ generateHash(data: string[]): Uint8Array {
const ctx = blake2bInit(32, undefined)
data.forEach(str => blake2bUpdate(ctx, Convert.hex2ab(str)))
return blake2bFinal(ctx)
{
"name": "nanocurrency-web",
- "version": "1.1.1",
+ "version": "1.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
{
"name": "nanocurrency-web",
- "version": "1.1.1",
+ "version": "1.2.0",
"description": "Toolkit for Nano cryptocurrency client side offline integrations",
"author": "Miro Metsänheimo <miro@metsanheimo.fi>",
"license": "MIT",
expect(result.accounts[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
})
+ it('should successfully import a legacy hex wallet with the a seed', () => {
+ const result = wallet.fromLegacySeed('0000000000000000000000000000000000000000000000000000000000000000')
+ expect(result).to.have.own.property('mnemonic')
+ expect(result).to.have.own.property('seed')
+ expect(result).to.have.own.property('accounts')
+ expect(result.mnemonic).to.be.undefined
+ expect(result.seed).to.equal('0000000000000000000000000000000000000000000000000000000000000000')
+ expect(result.accounts[0].privateKey).to.equal('9f0e444c69f77a49bd0be89db92c38fe713e0963165cca12faf5712d7657120f')
+ expect(result.accounts[0].publicKey).to.equal('c008b814a7d269a1fa3c6528b19201a24d797912db9996ff02a1ff356e45552b')
+ expect(result.accounts[0].address).to.equal('nano_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7')
+ })
+
+ it('should successfully import legacy hex accounts with the a seed', () => {
+ const accounts = wallet.legacyAccounts('0000000000000000000000000000000000000000000000000000000000000000', 0, 3)
+ expect(accounts[0]).to.have.own.property('accountIndex')
+ expect(accounts[0]).to.have.own.property('privateKey')
+ expect(accounts[0]).to.have.own.property('publicKey')
+ expect(accounts[0]).to.have.own.property('address')
+ expect(accounts).to.have.lengthOf(4)
+ expect(accounts[2].accountIndex).to.equal(2)
+ expect(accounts[2].privateKey).to.equal('6a1804198020b080996ba45b5891f8227d7a4f41c8479824423780d234939d58')
+ expect(accounts[2].publicKey).to.equal('2fea520fe54f5d0dca79d553d9c7f5af7db6ac17586dbca6905794caadc639df')
+ expect(accounts[2].address).to.equal('nano_1dzcca9ycmtx3q79mocmu95zdduxptp3gp5fqkmb1ownscpweggzah8cb4rb')
+ })
+
it('should throw when given a seed with an invalid length', () => {
expect(() => wallet.generate('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310')).to.throw(Error)
expect(() => wallet.generate('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310cd')).to.throw(Error)