]> zoso.dev Git - libnemo.git/commitdiff
Finalize implementation
authorMiro Metsänheimo <miro@metsanheimo.fi>
Sat, 12 Oct 2019 22:58:40 +0000 (01:58 +0300)
committerMiro Metsänheimo <miro@metsanheimo.fi>
Sat, 12 Oct 2019 22:58:40 +0000 (01:58 +0300)
* Add change block signing
* Add tests and changes based on the results
* Add some missing types

12 files changed:
index.ts
lib/address-generator.ts
lib/address-importer.ts
lib/bip32-key-derivation.ts
lib/bip39-mnemonic.ts
lib/block-signer.ts
lib/ed25519.ts
lib/util/convert.ts
lib/util/curve25519.ts
package-lock.json
package.json
test/test.js [new file with mode: 0644]

index 2fcfdf4db0ea83d7110e6eb3a2f52583fe8c6eb9..61789366d688628d4a98011036419928b6525b8b 100644 (file)
--- a/index.ts
+++ b/index.ts
@@ -1,6 +1,6 @@
 import { AddressGenerator } from './lib/address-generator'
-import { AddressImporter } from './lib/address-importer'
-import BlockSigner, { SendBlock, ReceiveBlock } from './lib/block-signer'
+import { AddressImporter, Account, Wallet } from './lib/address-importer'
+import BlockSigner, { TransactionBlock, RepresentativeBlock, SignedBlock } from './lib/block-signer'
 
 const generator = new AddressGenerator()
 const importer = new AddressImporter()
@@ -17,17 +17,17 @@ const wallet = {
         * The Nano address is derived from the public key using standard Nano encoding.
         * The address is prefixed with 'nano_'.
         *
-        * Generation uses CryptoJS to generate random entropy. You can give your own entropy
+        * Generation uses CryptoJS to generate random entropy by default. You can give your own entropy
         * as a parameter and it will be used instead.
         *
         * 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 entropy to be used instead of the default
+        * @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) => {
+       generate: (entropy?: string, seedPassword?: string): Wallet => {
                return generator.generateWallet(entropy, seedPassword)
        },
 
@@ -46,7 +46,7 @@ const wallet = {
         * @throws Throws an error if the mnemonic phrase doesn't pass validations
         * @returns the imported mnemonic, seed and account
         */
-       fromMnemonic: (mnemonic: string, seedPassword?: string) => {
+       fromMnemonic: (mnemonic: string, seedPassword?: string): Wallet => {
                return importer.fromMnemonic(mnemonic, seedPassword)
        },
 
@@ -63,7 +63,7 @@ const wallet = {
         * @param {string} seed The seed
         * @returns the importes seed and account
         */
-       fromSeed: (seed: string) => {
+       fromSeed: (seed: string): Wallet => {
                return importer.fromSeed(seed)
        },
 
@@ -78,7 +78,7 @@ const wallet = {
         * @param {number} from The start index
         * @param {number} to The end index
         */
-       accounts: (seed: string, from: number, to: number) => {
+       accounts: (seed: string, from: number, to: number): Account[] => {
                return importer.fromSeed(seed, from, to).accounts
        },
 
@@ -90,28 +90,53 @@ const block = {
        /**
         * Sign a send block with the input parameters
         *
+        * For a receive block, put your own address to the 'toAddress' property.
+        * All the NANO amounts should be input in RAW format. The addresses should be
+        * valid Nano addresses. Fetch the current balance, frontier (previous block) and
+        * representative address from the blockchain and generate work for the signature.
+        *
+        * The return value of this function is ready to be published to the blockchain.
+        *
+        * NOTICE: Always fetch up-to-date account info from the blockchain
+        *         before signing the block
+        *
         * @param {SendBlock} data The data for the block
         * @param {string} privateKey Private key to sign the block
         */
-       send: (data: SendBlock, privateKey: string) => {
-               return blockSigner.send(data, privateKey)
+       sign: (data: TransactionBlock, privateKey: string): SignedBlock => {
+               return blockSigner.sign(data, privateKey)
        },
 
        /**
-        * Sign a receive block with the input parameters
+        * Sign a representative change block with the input parameters
         *
-        * @param {SendBlock} data The data for the block
+        * For a change block, put your own address to the 'address' property.
+        * All the NANO amounts should be input in RAW format. The addresses should be
+        * valid Nano addresses. Fetch the current balance, previous block from the
+        * blockchain and generate work for the signature. Set the new representative address
+        * as the representative.
+        *
+        * NOTICE: Always fetch up-to-date account info from the blockchain
+        *         before signing the block
+        *
+        * @param {RepresentativeBlock} data The data for the block
         * @param {string} privateKey Private key to sign the block
+        *
         */
-       receive: (data: ReceiveBlock, privateKey: string) => {
-               return blockSigner.receive(data, privateKey)
-       },
+       representative: (data: RepresentativeBlock, privateKey: string): SignedBlock => {
+               const block: TransactionBlock = {
+                       ...data,
+                       fromAddress: data.address,
+                       amountRaw: '0',
+                       toAddress: 'nano_1111111111111111111111111111111111111111111111111111hifc8npp' // Burn address
+               }
 
-       // TODO: change representative block
+               return blockSigner.sign(block, privateKey)
+       },
 
 }
 
-export default {
+export {
        wallet,
        block,
 }
index b58ed6052e9ac0cb7ecd2a92f1858b86d247ea4d..629cc95286a3aa4f8f5c4990656475cb8dc665b7 100644 (file)
@@ -2,6 +2,7 @@ import Bip32KeyDerivation from './bip32-key-derivation'
 import Bip39Mnemonic from './bip39-mnemonic'
 import { Ed25519 } from './ed25519'
 import { NanoAddress } from './nano-address'
+import { Wallet } from './address-importer'
 
 export class AddressGenerator {
 
@@ -10,7 +11,7 @@ export class AddressGenerator {
         *
         * @param {String} seedPassword Password for the seed
         */
-       generateWallet(entropy = '', seedPassword: string = '') {
+       generateWallet(entropy = '', seedPassword: string = ''): Wallet {
                const bip39 = new Bip39Mnemonic(seedPassword)
                const wallet = bip39.createWallet(entropy)
 
index 5241ce9280e1a9b55d14894a6827afb5eafd4cc2..fe6418a3e784e2c0d72fb464e124e9f45bd9928f 100644 (file)
@@ -5,17 +5,24 @@ import { NanoAddress } from './nano-address'
 
 export class AddressImporter {
 
-       fromMnemonic(mnemonic: string, seedPassword = '') {
+       fromMnemonic(mnemonic: string, seedPassword = ''): Wallet {
                const bip39 = new Bip39Mnemonic(seedPassword)
                if (!bip39.validateMnemonic(mnemonic)) {
-                       throw 'Invalid mnemonic phrase'
+                       throw new Error('Invalid mnemonic phrase')
                }
 
                const seed = bip39.mnemonicToSeed(mnemonic)
                return this.nano(seed, 0, 0, mnemonic)
        }
 
-       fromSeed(seed: string, from = 0, to = 0) {
+       fromSeed(seed: string, from = 0, to = 0): Wallet {
+               if (seed.length !== 128) {
+                       throw new Error('Invalid seed length, must be a 128 byte hexadecimal string')
+               }
+               if (!/^[0-9a-f]+$/i.test(seed)) {
+                       throw new Error('Seed is not a valid hexadecimal string')
+               }
+
                return this.nano(seed, from, to, undefined)
        }
 
@@ -23,7 +30,7 @@ export class AddressImporter {
         * Generates the wallet
         * @param {String} seedPassword Password for the seed
         */
-       private nano(seed: string, from: number, to: number, mnemonic?: string) {
+       private nano(seed: string, from: number, to: number, mnemonic?: string): Wallet {
                const accounts = []
 
                for (let i = from; i <= to; i++) {
@@ -51,3 +58,16 @@ export class AddressImporter {
        }
 
 }
+
+export interface Wallet {
+       mnemonic: string
+       seed: string
+       accounts: Account[]
+}
+
+export interface Account {
+       accountIndex: number
+       privateKey: string
+       publicKey: string
+       address: string
+}
index 6d22086b47db0ed9ef317af197314a92ca00ecfc..5494dbe62888c124f53b0dd6a832ebbfb98495d0 100644 (file)
@@ -15,7 +15,7 @@ export default class Bip32KeyDerivation {
                this.seed = seed
        }
 
-       derivePath = () => {
+       derivePath = (): Chain => {
                const { key, chainCode } = this.getKeyFromSeed()
                const segments = this.path
                        .split('/')
@@ -28,13 +28,13 @@ export default class Bip32KeyDerivation {
                )
        }
 
-       private getKeyFromSeed = () => {
+       private getKeyFromSeed = (): Chain => {
                return this.derive(
                        CryptoJS.enc.Hex.parse(this.seed),
                        CryptoJS.enc.Utf8.parse(ED25519_CURVE))
        }
 
-       private CKDPriv = ({ key, chainCode }: { key: string, chainCode: string }, index: number) => {
+       private CKDPriv = ({ key, chainCode }: Chain, index: number) => {
                const ib = []
                ib.push((index >> 24) & 0xff)
                ib.push((index >> 16) & 0xff)
@@ -47,7 +47,7 @@ export default class Bip32KeyDerivation {
                        CryptoJS.enc.Hex.parse(chainCode))
        }
 
-       private derive = (data: string, base: string) => {
+       private derive = (data: string, base: string): Chain => {
                const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, base)
                const I = hmac.update(data).finalize().toString()
                const IL = I.slice(0, I.length / 2)
@@ -60,3 +60,8 @@ export default class Bip32KeyDerivation {
        }
 
 }
+
+export interface Chain {
+       key: string
+       chainCode: string
+}
index 8a598cee2ea3412ba70d43e195d2f77a14b71fde..8c9d7ca256206b2111902c8cf74e4c30c1f7fffd 100644 (file)
@@ -13,8 +13,13 @@ export default class Bip39Mnemonic {
        }
 
        createWallet = (entropy: string): { mnemonic: string, seed: string } => {
-               if (entropy.length !== 32) {
-                       throw 'Invalid entropy length'
+               if (entropy) {
+                       if (entropy.length !== 64) {
+                               throw new Error('Invalid entropy length, must be a 64 byte hexadecimal string')
+                       }
+                       if (!/^[0-9a-f]+$/i.test(entropy)) {
+                               throw new Error('Entopy is not a valid hexadecimal string')
+                       }
                }
 
                if (!entropy) {
index d3fdb8dc331266efe00dd8264c6685c749c9eeff..c1cd4f7f6de73645abe366d01ec9d29ea333cf53 100644 (file)
@@ -13,9 +13,14 @@ export default class BlockSigner {
 
        preamble = 0x6.toString().padStart(64, '0')
 
-       send(data: SendBlock, privateKey: string) {
+       sign(data: TransactionBlock, privateKey: string): SignedBlock {
+               if (!privateKey) {
+                       throw new Error('Please input the private key to sign the block')
+               }
+
                const balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
-               const newBalance = new BigNumber(balance).minus(new BigNumber(data.amount))
+               const amount = NanoConverter.convert(data.amountRaw, 'RAW', 'NANO')
+               const newBalance = new BigNumber(balance).minus(new BigNumber(amount))
                const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
                const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
                const account = this.nanoAddressToHexString(data.fromAddress)
@@ -38,36 +43,6 @@ export default class BlockSigner {
                }
        }
 
-       receive(data: ReceiveBlock, privateKey: string) {
-               let balance = '0'
-               if (data.walletBalanceRaw != '0') {
-                       balance = NanoConverter.convert(data.walletBalanceRaw, 'RAW', 'NANO')
-               }
-
-               const amountNano = NanoConverter.convert(data.amount, 'RAW', 'NANO')
-               const newBalance = new BigNumber(balance).plus(new BigNumber(amountNano))
-               const rawBalance = NanoConverter.convert(newBalance, 'NANO', 'RAW')
-               const hexBalance = Convert.dec2hex(rawBalance, 16).toUpperCase()
-               const account = this.nanoAddressToHexString(data.walletAddress)
-               const representative = this.nanoAddressToHexString(data.representativeAddress)
-               const link = data.hash
-
-               const signatureBytes = this.ed25519.sign(
-                       this.generateHash(this.preamble, account, data.frontier, representative, hexBalance, link),
-                       Convert.hex2ab(privateKey))
-
-               return {
-                       type: 'state',
-                       account: data.walletAddress,
-                       previous: data.frontier,
-                       representative: data.representativeAddress,
-                       balance: rawBalance,
-                       link,
-                       signature: Convert.ab2hex(signatureBytes),
-                       work: data.work,
-               }
-       }
-
        private generateHash(preamble: string, account: string, previous: string, representative: string, balance: string, link: string) {
                const ctx = blake.blake2bInit(32, undefined)
                blake.blake2bUpdate(ctx, Convert.hex2ab(preamble))
@@ -90,30 +65,39 @@ export default class BlockSigner {
                                const key = Convert.ab2hex(keyBytes).toUpperCase()
                                return key
                        }
-                       throw 'Checksum mismatch'
+                       throw new Error('Checksum mismatch')
                } else {
-                       throw 'Illegal characters'
+                       throw new Error('Illegal characters')
                }
        }
 
 }
 
-export interface SendBlock {
+export interface TransactionBlock {
        walletBalanceRaw: string
        fromAddress: string
        toAddress: string
        representativeAddress: string
        frontier: string
-       amount: string
+       amountRaw: string
        work: string
 }
 
-export interface ReceiveBlock {
+export interface RepresentativeBlock {
        walletBalanceRaw: string
-       walletAddress: string
+       address: string
        representativeAddress: string
        frontier: string
-       hash: string
-       amount: string
+       work: string
+}
+
+export interface SignedBlock {
+       type: 'state'
+       account: string
+       previous: string
+       representative: string
+       balance: string
+       link: string
+       signature: string
        work: string
 }
index b03b4844df711680e316889fa473c73905504eff..dfcbb6e98aec7da357f4d059270ef34289e87570 100644 (file)
@@ -98,7 +98,7 @@ export class Ed25519 {
         * @param {String} seed A 32 byte cryptographic secure random hexadecimal string. This is basically the secret key
         * @param {Object} Returns sk (Secret key) and pk (Public key) as 32 byte hexadecimal strings
         */
-       generateKeys(seed: string): { privateKey: string, publicKey: string } {
+       generateKeys(seed: string): KeyPair {
                const pk = new Uint8Array(32)
                const p = [this.curve.gf(), this.curve.gf(), this.curve.gf(), this.curve.gf()]
                const h = blake
@@ -218,34 +218,9 @@ export class Ed25519 {
                return 0
        }
 
-       /**
-        * TODO: Replace sha512 with blakejs
-        * Verify a message signature
-        * @param {Uint8Array} msg Message to be signed as byte array
-        * @param {Uint8Array} pk Public key as 32 byte array
-        * @param {Uint8Array} sig Signature as 64 byte array
-        * @param {Boolean} Returns true if signature is valid
-        */
-       // verify(msg, pk, sig) {
-       //      let CURVE = this.curve
-       //      let p = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()],
-       //              q = [CURVE.gf(), CURVE.gf(), CURVE.gf(), CURVE.gf()]
-
-       //      if (sig.length !== 64) return false
-       //      if (pk.length !== 32) return false
-       //      if (CURVE.unpackNeg(q, pk)) return false
-
-       //      // compute k = SHA512(R || A || M)
-       //      let k = this.sha512.init().update(sig.subarray(0, 32)).update(pk).digest(msg)
-       //      this.reduce(k)
-       //      this.scalarmult(p, q, k)
-
-       //      let t = new Uint8Array(32)
-       //      this.scalarbase(q, sig.subarray(32))
-       //      CURVE.add(p, q)
-       //      this.pack(t, p)
-
-       //      return Util.compare(sig.subarray(0, 32), t)
-       // }
-
 }
+
+export interface KeyPair {
+       privateKey: string
+       publicKey: string
+}
\ No newline at end of file
index 490f6f0b6e46afbe8f9aeae6bda67c4f4d5bb263..dad46e9e1f0b0756b6755c70d0e7c8d225d80e03 100644 (file)
@@ -65,12 +65,7 @@ export class Convert {
                let s: number
 
                while (decimals.length) {
-                       const dec = decimals.shift()
-                       if (!dec) {
-                               throw 'Invalid decimal'
-                       }
-
-                       s = 1 * +dec
+                       s = 1 * +decimals.shift()
                        for (i = 0; s || i < sum.length; i++) {
                                s += (sum[i] || 0) * 10
                                sum[i] = s % 16
@@ -79,12 +74,7 @@ export class Convert {
                }
 
                while (sum.length) {
-                       const dec = sum.pop()
-                       if (!dec) {
-                               throw 'Invalid decimal'
-                       }
-
-                       hex.push(dec.toString(16))
+                       hex.push(sum.pop().toString(16))
                }
 
                let joined = hex.join('')
index 56a9a69e36c40a770af1d3a1d15060a1806fd4d8..b006acbb7af04100d5ea2e0cddfb99037b82bc7b 100644 (file)
@@ -693,7 +693,7 @@ export class Curve25519 {
                const sk = seed.slice()
                const pk = new Uint8Array(32)
                if (sk.length !== 32) {
-                       throw 'Invalid secret key size, expected 32 bytes'
+                       throw new Error('Invalid secret key size, expected 32 bytes')
                }
 
                sk[0] &= 0xf8
index a71d636b54f0816b015e95b8b4ec96f4d598c2e0..1f9c676adfff4ad4e53ecc0742cff830e21fcdd2 100644 (file)
                        "integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==",
                        "dev": true
                },
+               "ansi-colors": {
+                       "version": "3.2.3",
+                       "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+                       "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+                       "dev": true
+               },
+               "ansi-regex": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+                       "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+                       "dev": true
+               },
+               "ansi-styles": {
+                       "version": "3.2.1",
+                       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+                       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+                       "dev": true,
+                       "requires": {
+                               "color-convert": "1.9.3"
+                       }
+               },
+               "argparse": {
+                       "version": "1.0.10",
+                       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+                       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+                       "dev": true,
+                       "requires": {
+                               "sprintf-js": "1.0.3"
+                       }
+               },
+               "assertion-error": {
+                       "version": "1.1.0",
+                       "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+                       "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+                       "dev": true
+               },
+               "balanced-match": {
+                       "version": "1.0.0",
+                       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+                       "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+                       "dev": true
+               },
                "bignumber": {
                        "version": "1.1.0",
                        "resolved": "https://registry.npmjs.org/bignumber/-/bignumber-1.1.0.tgz",
                        "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
                        "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
                },
+               "brace-expansion": {
+                       "version": "1.1.11",
+                       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+                       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+                       "dev": true,
+                       "requires": {
+                               "balanced-match": "1.0.0",
+                               "concat-map": "0.0.1"
+                       }
+               },
+               "browser-stdout": {
+                       "version": "1.3.1",
+                       "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+                       "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+                       "dev": true
+               },
+               "camelcase": {
+                       "version": "5.3.1",
+                       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+                       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+                       "dev": true
+               },
+               "chai": {
+                       "version": "4.2.0",
+                       "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+                       "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+                       "dev": true,
+                       "requires": {
+                               "assertion-error": "1.1.0",
+                               "check-error": "1.0.2",
+                               "deep-eql": "3.0.1",
+                               "get-func-name": "2.0.0",
+                               "pathval": "1.1.0",
+                               "type-detect": "4.0.8"
+                       }
+               },
+               "chalk": {
+                       "version": "2.4.2",
+                       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+                       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+                       "dev": true,
+                       "requires": {
+                               "ansi-styles": "3.2.1",
+                               "escape-string-regexp": "1.0.5",
+                               "supports-color": "5.5.0"
+                       },
+                       "dependencies": {
+                               "supports-color": {
+                                       "version": "5.5.0",
+                                       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+                                       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+                                       "dev": true,
+                                       "requires": {
+                                               "has-flag": "3.0.0"
+                                       }
+                               }
+                       }
+               },
+               "check-error": {
+                       "version": "1.0.2",
+                       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+                       "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+                       "dev": true
+               },
+               "cliui": {
+                       "version": "5.0.0",
+                       "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+                       "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+                       "dev": true,
+                       "requires": {
+                               "string-width": "3.1.0",
+                               "strip-ansi": "5.2.0",
+                               "wrap-ansi": "5.1.0"
+                       },
+                       "dependencies": {
+                               "ansi-regex": {
+                                       "version": "4.1.0",
+                                       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                                       "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                                       "dev": true
+                               },
+                               "string-width": {
+                                       "version": "3.1.0",
+                                       "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                                       "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                                       "dev": true,
+                                       "requires": {
+                                               "emoji-regex": "7.0.3",
+                                               "is-fullwidth-code-point": "2.0.0",
+                                               "strip-ansi": "5.2.0"
+                                       }
+                               },
+                               "strip-ansi": {
+                                       "version": "5.2.0",
+                                       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                                       "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                                       "dev": true,
+                                       "requires": {
+                                               "ansi-regex": "4.1.0"
+                                       }
+                               }
+                       }
+               },
+               "color-convert": {
+                       "version": "1.9.3",
+                       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+                       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+                       "dev": true,
+                       "requires": {
+                               "color-name": "1.1.3"
+                       }
+               },
+               "color-name": {
+                       "version": "1.1.3",
+                       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+                       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+                       "dev": true
+               },
+               "concat-map": {
+                       "version": "0.0.1",
+                       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+                       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+                       "dev": true
+               },
+               "crypto-js": {
+                       "version": "3.1.9-1",
+                       "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz",
+                       "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
+               },
+               "debug": {
+                       "version": "3.2.6",
+                       "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+                       "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+                       "dev": true,
+                       "requires": {
+                               "ms": "2.1.1"
+                       }
+               },
+               "decamelize": {
+                       "version": "1.2.0",
+                       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+                       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+                       "dev": true
+               },
+               "deep-eql": {
+                       "version": "3.0.1",
+                       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+                       "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+                       "dev": true,
+                       "requires": {
+                               "type-detect": "4.0.8"
+                       }
+               },
+               "define-properties": {
+                       "version": "1.1.3",
+                       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+                       "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+                       "dev": true,
+                       "requires": {
+                               "object-keys": "1.1.1"
+                       }
+               },
+               "diff": {
+                       "version": "3.5.0",
+                       "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+                       "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+                       "dev": true
+               },
+               "emoji-regex": {
+                       "version": "7.0.3",
+                       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+                       "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+                       "dev": true
+               },
+               "es-abstract": {
+                       "version": "1.15.0",
+                       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz",
+                       "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==",
+                       "dev": true,
+                       "requires": {
+                               "es-to-primitive": "1.2.0",
+                               "function-bind": "1.1.1",
+                               "has": "1.0.3",
+                               "has-symbols": "1.0.0",
+                               "is-callable": "1.1.4",
+                               "is-regex": "1.0.4",
+                               "object-inspect": "1.6.0",
+                               "object-keys": "1.1.1",
+                               "string.prototype.trimleft": "2.1.0",
+                               "string.prototype.trimright": "2.1.0"
+                       }
+               },
+               "es-to-primitive": {
+                       "version": "1.2.0",
+                       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+                       "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+                       "dev": true,
+                       "requires": {
+                               "is-callable": "1.1.4",
+                               "is-date-object": "1.0.1",
+                               "is-symbol": "1.0.2"
+                       }
+               },
+               "escape-string-regexp": {
+                       "version": "1.0.5",
+                       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+                       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+                       "dev": true
+               },
+               "esprima": {
+                       "version": "4.0.1",
+                       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+                       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+                       "dev": true
+               },
+               "find-up": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+                       "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+                       "dev": true,
+                       "requires": {
+                               "locate-path": "3.0.0"
+                       }
+               },
+               "flat": {
+                       "version": "4.1.0",
+                       "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+                       "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+                       "dev": true,
+                       "requires": {
+                               "is-buffer": "2.0.4"
+                       }
+               },
+               "fs.realpath": {
+                       "version": "1.0.0",
+                       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+                       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+                       "dev": true
+               },
+               "function-bind": {
+                       "version": "1.1.1",
+                       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+                       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+                       "dev": true
+               },
+               "get-caller-file": {
+                       "version": "2.0.5",
+                       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+                       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+                       "dev": true
+               },
+               "get-func-name": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+                       "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+                       "dev": true
+               },
+               "glob": {
+                       "version": "7.1.3",
+                       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+                       "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+                       "dev": true,
+                       "requires": {
+                               "fs.realpath": "1.0.0",
+                               "inflight": "1.0.6",
+                               "inherits": "2.0.4",
+                               "minimatch": "3.0.4",
+                               "once": "1.4.0",
+                               "path-is-absolute": "1.0.1"
+                       }
+               },
+               "growl": {
+                       "version": "1.10.5",
+                       "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+                       "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+                       "dev": true
+               },
+               "has": {
+                       "version": "1.0.3",
+                       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+                       "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+                       "dev": true,
+                       "requires": {
+                               "function-bind": "1.1.1"
+                       }
+               },
+               "has-flag": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+                       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+                       "dev": true
+               },
+               "has-symbols": {
+                       "version": "1.0.0",
+                       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+                       "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+                       "dev": true
+               },
+               "he": {
+                       "version": "1.2.0",
+                       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+                       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+                       "dev": true
+               },
+               "inflight": {
+                       "version": "1.0.6",
+                       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+                       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+                       "dev": true,
+                       "requires": {
+                               "once": "1.4.0",
+                               "wrappy": "1.0.2"
+                       }
+               },
+               "inherits": {
+                       "version": "2.0.4",
+                       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+                       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+                       "dev": true
+               },
+               "is-buffer": {
+                       "version": "2.0.4",
+                       "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+                       "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+                       "dev": true
+               },
+               "is-callable": {
+                       "version": "1.1.4",
+                       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+                       "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+                       "dev": true
+               },
+               "is-date-object": {
+                       "version": "1.0.1",
+                       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+                       "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+                       "dev": true
+               },
+               "is-fullwidth-code-point": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                       "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                       "dev": true
+               },
+               "is-regex": {
+                       "version": "1.0.4",
+                       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+                       "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+                       "dev": true,
+                       "requires": {
+                               "has": "1.0.3"
+                       }
+               },
+               "is-symbol": {
+                       "version": "1.0.2",
+                       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+                       "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+                       "dev": true,
+                       "requires": {
+                               "has-symbols": "1.0.0"
+                       }
+               },
+               "isexe": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+                       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+                       "dev": true
+               },
+               "js-yaml": {
+                       "version": "3.13.1",
+                       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+                       "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+                       "dev": true,
+                       "requires": {
+                               "argparse": "1.0.10",
+                               "esprima": "4.0.1"
+                       }
+               },
+               "locate-path": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+                       "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+                       "dev": true,
+                       "requires": {
+                               "p-locate": "3.0.0",
+                               "path-exists": "3.0.0"
+                       }
+               },
+               "lodash": {
+                       "version": "4.17.15",
+                       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+                       "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+                       "dev": true
+               },
+               "log-symbols": {
+                       "version": "2.2.0",
+                       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+                       "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+                       "dev": true,
+                       "requires": {
+                               "chalk": "2.4.2"
+                       }
+               },
+               "minimatch": {
+                       "version": "3.0.4",
+                       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+                       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+                       "dev": true,
+                       "requires": {
+                               "brace-expansion": "1.1.11"
+                       }
+               },
+               "minimist": {
+                       "version": "0.0.8",
+                       "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+                       "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+                       "dev": true
+               },
+               "mkdirp": {
+                       "version": "0.5.1",
+                       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+                       "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+                       "dev": true,
+                       "requires": {
+                               "minimist": "0.0.8"
+                       }
+               },
+               "mocha": {
+                       "version": "6.2.1",
+                       "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.1.tgz",
+                       "integrity": "sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A==",
+                       "dev": true,
+                       "requires": {
+                               "ansi-colors": "3.2.3",
+                               "browser-stdout": "1.3.1",
+                               "debug": "3.2.6",
+                               "diff": "3.5.0",
+                               "escape-string-regexp": "1.0.5",
+                               "find-up": "3.0.0",
+                               "glob": "7.1.3",
+                               "growl": "1.10.5",
+                               "he": "1.2.0",
+                               "js-yaml": "3.13.1",
+                               "log-symbols": "2.2.0",
+                               "minimatch": "3.0.4",
+                               "mkdirp": "0.5.1",
+                               "ms": "2.1.1",
+                               "node-environment-flags": "1.0.5",
+                               "object.assign": "4.1.0",
+                               "strip-json-comments": "2.0.1",
+                               "supports-color": "6.0.0",
+                               "which": "1.3.1",
+                               "wide-align": "1.1.3",
+                               "yargs": "13.3.0",
+                               "yargs-parser": "13.1.1",
+                               "yargs-unparser": "1.6.0"
+                       }
+               },
+               "ms": {
+                       "version": "2.1.1",
+                       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+                       "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+                       "dev": true
+               },
+               "node-environment-flags": {
+                       "version": "1.0.5",
+                       "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
+                       "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
+                       "dev": true,
+                       "requires": {
+                               "object.getownpropertydescriptors": "2.0.3",
+                               "semver": "5.7.1"
+                       }
+               },
+               "object-inspect": {
+                       "version": "1.6.0",
+                       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
+                       "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
+                       "dev": true
+               },
+               "object-keys": {
+                       "version": "1.1.1",
+                       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+                       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+                       "dev": true
+               },
+               "object.assign": {
+                       "version": "4.1.0",
+                       "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+                       "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+                       "dev": true,
+                       "requires": {
+                               "define-properties": "1.1.3",
+                               "function-bind": "1.1.1",
+                               "has-symbols": "1.0.0",
+                               "object-keys": "1.1.1"
+                       }
+               },
+               "object.getownpropertydescriptors": {
+                       "version": "2.0.3",
+                       "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
+                       "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+                       "dev": true,
+                       "requires": {
+                               "define-properties": "1.1.3",
+                               "es-abstract": "1.15.0"
+                       }
+               },
+               "once": {
+                       "version": "1.4.0",
+                       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+                       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+                       "dev": true,
+                       "requires": {
+                               "wrappy": "1.0.2"
+                       }
+               },
+               "p-limit": {
+                       "version": "2.2.1",
+                       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+                       "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
+                       "dev": true,
+                       "requires": {
+                               "p-try": "2.2.0"
+                       }
+               },
+               "p-locate": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+                       "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+                       "dev": true,
+                       "requires": {
+                               "p-limit": "2.2.1"
+                       }
+               },
+               "p-try": {
+                       "version": "2.2.0",
+                       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+                       "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+                       "dev": true
+               },
+               "path-exists": {
+                       "version": "3.0.0",
+                       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+                       "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+                       "dev": true
+               },
+               "path-is-absolute": {
+                       "version": "1.0.1",
+                       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+                       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+                       "dev": true
+               },
+               "pathval": {
+                       "version": "1.1.0",
+                       "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+                       "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+                       "dev": true
+               },
+               "require-directory": {
+                       "version": "2.1.1",
+                       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+                       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+                       "dev": true
+               },
+               "require-main-filename": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+                       "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+                       "dev": true
+               },
+               "semver": {
+                       "version": "5.7.1",
+                       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                       "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                       "dev": true
+               },
+               "set-blocking": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+                       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+                       "dev": true
+               },
+               "sprintf-js": {
+                       "version": "1.0.3",
+                       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+                       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+                       "dev": true
+               },
+               "string-width": {
+                       "version": "2.1.1",
+                       "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+                       "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+                       "dev": true,
+                       "requires": {
+                               "is-fullwidth-code-point": "2.0.0",
+                               "strip-ansi": "4.0.0"
+                       }
+               },
+               "string.prototype.trimleft": {
+                       "version": "2.1.0",
+                       "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
+                       "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+                       "dev": true,
+                       "requires": {
+                               "define-properties": "1.1.3",
+                               "function-bind": "1.1.1"
+                       }
+               },
+               "string.prototype.trimright": {
+                       "version": "2.1.0",
+                       "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
+                       "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+                       "dev": true,
+                       "requires": {
+                               "define-properties": "1.1.3",
+                               "function-bind": "1.1.1"
+                       }
+               },
+               "strip-ansi": {
+                       "version": "4.0.0",
+                       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+                       "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+                       "dev": true,
+                       "requires": {
+                               "ansi-regex": "3.0.0"
+                       }
+               },
+               "strip-json-comments": {
+                       "version": "2.0.1",
+                       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+                       "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+                       "dev": true
+               },
+               "supports-color": {
+                       "version": "6.0.0",
+                       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+                       "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+                       "dev": true,
+                       "requires": {
+                               "has-flag": "3.0.0"
+                       }
+               },
+               "type-detect": {
+                       "version": "4.0.8",
+                       "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+                       "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+                       "dev": true
+               },
                "typescript": {
                        "version": "3.6.3",
                        "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
                        "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
                        "dev": true
+               },
+               "which": {
+                       "version": "1.3.1",
+                       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+                       "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+                       "dev": true,
+                       "requires": {
+                               "isexe": "2.0.0"
+                       }
+               },
+               "which-module": {
+                       "version": "2.0.0",
+                       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+                       "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+                       "dev": true
+               },
+               "wide-align": {
+                       "version": "1.1.3",
+                       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+                       "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+                       "dev": true,
+                       "requires": {
+                               "string-width": "2.1.1"
+                       }
+               },
+               "wrap-ansi": {
+                       "version": "5.1.0",
+                       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+                       "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+                       "dev": true,
+                       "requires": {
+                               "ansi-styles": "3.2.1",
+                               "string-width": "3.1.0",
+                               "strip-ansi": "5.2.0"
+                       },
+                       "dependencies": {
+                               "ansi-regex": {
+                                       "version": "4.1.0",
+                                       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                                       "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                                       "dev": true
+                               },
+                               "string-width": {
+                                       "version": "3.1.0",
+                                       "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                                       "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                                       "dev": true,
+                                       "requires": {
+                                               "emoji-regex": "7.0.3",
+                                               "is-fullwidth-code-point": "2.0.0",
+                                               "strip-ansi": "5.2.0"
+                                       }
+                               },
+                               "strip-ansi": {
+                                       "version": "5.2.0",
+                                       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                                       "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                                       "dev": true,
+                                       "requires": {
+                                               "ansi-regex": "4.1.0"
+                                       }
+                               }
+                       }
+               },
+               "wrappy": {
+                       "version": "1.0.2",
+                       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+                       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+                       "dev": true
+               },
+               "y18n": {
+                       "version": "4.0.0",
+                       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+                       "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+                       "dev": true
+               },
+               "yargs": {
+                       "version": "13.3.0",
+                       "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+                       "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+                       "dev": true,
+                       "requires": {
+                               "cliui": "5.0.0",
+                               "find-up": "3.0.0",
+                               "get-caller-file": "2.0.5",
+                               "require-directory": "2.1.1",
+                               "require-main-filename": "2.0.0",
+                               "set-blocking": "2.0.0",
+                               "string-width": "3.1.0",
+                               "which-module": "2.0.0",
+                               "y18n": "4.0.0",
+                               "yargs-parser": "13.1.1"
+                       },
+                       "dependencies": {
+                               "ansi-regex": {
+                                       "version": "4.1.0",
+                                       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                                       "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                                       "dev": true
+                               },
+                               "string-width": {
+                                       "version": "3.1.0",
+                                       "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                                       "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                                       "dev": true,
+                                       "requires": {
+                                               "emoji-regex": "7.0.3",
+                                               "is-fullwidth-code-point": "2.0.0",
+                                               "strip-ansi": "5.2.0"
+                                       }
+                               },
+                               "strip-ansi": {
+                                       "version": "5.2.0",
+                                       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                                       "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                                       "dev": true,
+                                       "requires": {
+                                               "ansi-regex": "4.1.0"
+                                       }
+                               }
+                       }
+               },
+               "yargs-parser": {
+                       "version": "13.1.1",
+                       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+                       "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+                       "dev": true,
+                       "requires": {
+                               "camelcase": "5.3.1",
+                               "decamelize": "1.2.0"
+                       }
+               },
+               "yargs-unparser": {
+                       "version": "1.6.0",
+                       "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+                       "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+                       "dev": true,
+                       "requires": {
+                               "flat": "4.1.0",
+                               "lodash": "4.17.15",
+                               "yargs": "13.3.0"
+                       }
                }
        }
 }
index c76f3493de96cb73fc917d69fb4c9821d699eea4..c5e41bfc6d23324e211fc973efbc485437829807 100644 (file)
        "main": "index.js",
        "scripts": {
                "build": "tsc",
-               "test": "echo \"Error: no test specified\" && exit 1"
+               "test": "mocha --reporter spec"
        },
        "dependencies": {
                "bignumber": "1.1.0",
-               "blakejs": "1.1.0"
+               "blakejs": "1.1.0",
+               "crypto-js": "3.1.9-1"
        },
        "devDependencies": {
                "@types/bignumber.js": "5.0.0",
                "@types/node": "12.7.12",
+               "chai": "4.2.0",
+               "mocha": "6.2.1",
                "typescript": "3.6.3"
        }
 }
diff --git a/test/test.js b/test/test.js
new file mode 100644 (file)
index 0000000..ef0f00b
--- /dev/null
@@ -0,0 +1,156 @@
+'use strict'
+
+const expect = require('chai').expect
+const { wallet, block } = require('../dist/index')
+
+// WARNING: Do not send any funds to the test vectors below
+describe('generate wallet test', () => {
+
+       it('should generate wallet with random entropy', () => {
+               const result = wallet.generate()
+               expect(result).to.have.own.property('mnemonic')
+               expect(result).to.have.own.property('seed')
+               expect(result).to.have.own.property('accounts')
+       })
+
+       it('should generate the correct wallet with the given test vector', () => {
+               const result = wallet.generate('6caf5a42bb8074314aae20295975ece663be7aad945a73613d193b0cc41c7970')
+               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.equal('hole kiss mouse jacket also board click series citizen slight kite smoke desk diary rent mercy inflict antique edge invite slush athlete total brain')
+               expect(result.seed).to.equal('1accdd4c25e06e47310d0c62c290ec166071d024352e003e5366e8ba6ba523f2a0cb34116ac55a238a886778880a9b2a547112fd7cffade81d8d8d084ccb7d36')
+               expect(result.accounts[0].privateKey).to.equal('eb18b748bcc48f824cf8a1fe92f7fc93bfc6f2a1eb9c1d40fa26d335d8a0c30f')
+               expect(result.accounts[0].publicKey).to.equal('a9ef7bbc004813cf75c5fc5c582066182d5c9cffd42eb7eb81cefea8e78c47c5')
+               expect(result.accounts[0].address).to.equal('nano_3chhhgy11k1msxtwdz4wd1i8e83fdkghzo3gpzor5mqyo5mrrjy79zpw1g34')
+       })
+
+       it('should generate the correct wallet with the given test vector and a seed password', () => {
+               // Using the same entropy as before, but a different password
+               const result = wallet.generate('6caf5a42bb8074314aae20295975ece663be7aad945a73613d193b0cc41c7970', 'some password')
+               expect(result).to.have.own.property('mnemonic')
+               expect(result).to.have.own.property('seed')
+               expect(result).to.have.own.property('accounts')
+
+               // Should result in the same mnemonic, but different seed and account
+               expect(result.mnemonic).to.equal('hole kiss mouse jacket also board click series citizen slight kite smoke desk diary rent mercy inflict antique edge invite slush athlete total brain')
+               expect(result.seed).to.not.equal('1accdd4c25e06e47310d0c62c290ec166071d024352e003e5366e8ba6ba523f2a0cb34116ac55a238a886778880a9b2a547112fd7cffade81d8d8d084ccb7d36')
+               expect(result.accounts[0].privateKey).to.not.equal('eb18b748bcc48f824cf8a1fe92f7fc93bfc6f2a1eb9c1d40fa26d335d8a0c30f')
+               expect(result.accounts[0].publicKey).to.not.equal('a9ef7bbc004813cf75c5fc5c582066182d5c9cffd42eb7eb81cefea8e78c47c5')
+               expect(result.accounts[0].address).to.not.equal('nano_3chhhgy11k1msxtwdz4wd1i8e83fdkghzo3gpzor5mqyo5mrrjy79zpw1g34')
+
+               expect(result.seed).to.equal('146e3e2a0530848c9174d45ecec8c3f74a7be3f1ee832f92eb6227284121eb2e48a6b8fc469403984cd5e8f0d1ed05777c78f458d0e98c911841590e5d645dc3')
+               expect(result.accounts[0].privateKey).to.equal('2d5851bd5a89b8c943078be6ad5bbee8aeab77d6a4744c20d1b87d78e3286b93')
+               expect(result.accounts[0].publicKey).to.equal('923b6c7e281c1c5529fd2dc848117781216a1753cfd487fc34009f3591e636d7')
+               expect(result.accounts[0].address).to.equal('nano_36jufjz4i91wcnnztdgab1aqh1b3fado9mynizy5a16z8payefpqo81zsshc')
+       })
+
+       it('should throw when given an entropy with an invalid length', () => {
+               expect(() => wallet.generate('6caf5a42bb8074314aae20295975ece663be7aad945a73613d193b0cc41c797')).to.throw(Error)
+               expect(() => wallet.generate('6caf5a42bb8074314aae20295975ece663be7aad945a73613d193b0cc41c79701')).to.throw(Error)
+       })
+
+       it('should throw when given an entropy containing non-hex characters', () => {
+               expect(() => wallet.generate('6gaf5a42bb8074314aae20295975ece663be7aad945a73613d193b0cc41c7970')).to.throw(Error)
+       })
+
+})
+
+// Test vectors from https://docs.nano.org/integration-guides/key-management/
+describe('import wallet with official test vectors test', () => {
+
+       it('should successfully import a wallet with the official Nano test vectors mnemonic', () => {
+               const result = wallet.fromMnemonic(
+                       'edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur',
+                       'some password')
+               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.equal('edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur')
+               expect(result.seed).to.equal('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c')
+               expect(result.accounts[0].privateKey).to.equal('3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143')
+               expect(result.accounts[0].publicKey).to.equal('5b65b0e8173ee0802c2c3e6c9080d1a16b06de1176c938a924f58670904e82c4')
+               expect(result.accounts[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
+       })
+
+       it('should successfully import a wallet with the official Nano test vectors seed', () => {
+               const result = wallet.fromSeed('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c')
+               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('0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c')
+               expect(result.accounts[0].privateKey).to.equal('3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143')
+               expect(result.accounts[0].publicKey).to.equal('5b65b0e8173ee0802c2c3e6c9080d1a16b06de1176c938a924f58670904e82c4')
+               expect(result.accounts[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
+       })
+
+       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)
+       })
+
+       it('should throw when given a seed containing non-hex characters', () => {
+               expect(() => wallet.generate('0gc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c')).to.throw(Error)
+       })
+
+})
+
+describe('derive more accounts from the same seed test', () => {
+
+       it('should derive accounts from the given seed', () => {
+               const result = wallet.accounts(
+                       '0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c',
+                       0, 14)
+               expect(result.length).to.equal(15)
+               expect(result[0].privateKey).to.equal('3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143')
+               expect(result[0].publicKey).to.equal('5b65b0e8173ee0802c2c3e6c9080d1a16b06de1176c938a924f58670904e82c4')
+               expect(result[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
+               expect(result[14].privateKey).to.equal('5f12e37c64daf2501c6a6a20614fd8d977fed65b5b5f0b045ec997f2ed2f53ca')
+               expect(result[14].publicKey).to.equal('f93a61018e07825a095e8cf7bdce9242e9c12c5c41a55de597a2be93fa41306b')
+               expect(result[14].address).to.equal('nano_3ybte61rw3w4da6ox59qqq9b6iqbr6p7rif7dqkshaoykhx64e5dbp4o1ua1')
+       })
+
+})
+
+// Test vectors from https://docs.nano.org/integration-guides/key-management/
+describe('block signing tests using official test vectors', () => {
+
+       it('should create a valid signature for a receive block', () => {
+               const result = block.sign({
+                       walletBalanceRaw: '18618869000000000000000000000000',
+                       fromAddress: 'nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx',
+                       toAddress: 'nano_3kyb49tqpt39ekc49kbej51ecsjqnimnzw1swxz4boix4ctm93w517umuiw8',
+                       representativeAddress: 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou',
+                       frontier: '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',
+                       amountRaw: '7000000000000000000000000000000',
+                       work: 'c5cf86de24b24419',
+               }, '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3')
+               expect(result.signature.toUpperCase()).to.equal('EEFFE1EFCCC8F2F6F2F1B79B80ABE855939DD9D6341323186494ADEE775DAADB3B6A6A07A85511F2185F6E739C4A54F1454436E22255A542ED879FD04FEED001')
+       })
+
+       it('should create a valid signature for a send block', () => {
+               const result = block.sign({
+                       walletBalanceRaw: '5618869000000000000000000000000',
+                       fromAddress: 'nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx',
+                       toAddress: 'nano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p',
+                       representativeAddress: 'nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou',
+                       frontier: '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D',
+                       amountRaw: '2000000000000000000000000000000',
+                       work: 'fbffed7c73b61367',
+               }, '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3')
+               expect(result.signature.toUpperCase()).to.equal('79240D56231EF1885F354473733AF158DC6DA50E53836179565A20C0BE89D473ED3FF8CD11545FF0ED162A0B2C4626FD6BF84518568F8BB965A4884C7C32C205')
+       })
+
+       it('should create a valid signature for a change rep block', () => {
+               const result = block.representative({
+                       walletBalanceRaw: '3000000000000000000000000000000',
+                       address: 'nano_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php',
+                       representativeAddress: 'nano_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs',
+                       frontier: '128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4',
+                       work: '0000000000000000',
+               }, '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3') // Did not find a private key at nano docs for this address
+               expect(result.signature.toUpperCase()).to.equal('A3C3C66D6519CBC0A198E56855942DEACC6EF741021A1B11279269ADC587DE1DA53CD478B8A47553231104CF24D742E1BB852B0546B87038C19BAE20F9082B0D')
+       })
+
+})