]> zoso.dev Git - libnemo.git/commitdiff
Version 1.2.1
authorMiro Metsänheimo <miro@metsanheimo.fi>
Wed, 27 May 2020 20:03:12 +0000 (23:03 +0300)
committerMiro Metsänheimo <miro@metsanheimo.fi>
Wed, 27 May 2020 20:07:16 +0000 (23:07 +0300)
* Fixed an issue where address importer threw an error on mnemonic words
that had a checksum hex value with a leading zero
* Added the possibility to validate mnemonic words and nano addresses

README.md
index.ts
lib/address-importer.ts
lib/bip39-mnemonic.ts
lib/nano-address.ts
package-lock.json
package.json
test/test.js

index d5c380d3d153d54da9e2d6e56d0ad6b05d78d207..b78e481232bfc9aa0fe2e3c3283ef48c45b57143 100644 (file)
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ The toolkit supports creating and importing wallets and signing blocks on-device
 * Runs in all web browsers and mobile frameworks built with Javascript
 * Convert Nano units
 * Sign any strings with the private key, for example using a password for the user created from the user ID.
+* Validate addresses and mnemonic words
 
 ---
 
@@ -189,15 +190,27 @@ For example implementing client side login with the password being the user's e-
 ```javascript
 import { tools } from 'nanocurrency-web'
 
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3';
+const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
 const signed = tools.sign(privateKey, 'foo@bar.com')
 ```
 
+#### Validating values
+
+```javascript
+import { tools } from 'nanocurrency-web'
+
+// Validate Nano address
+const valid = tools.validateAddress('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
+
+// Validate mnemonic words
+const valid = tools.validateMnemonic('edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur')
+```
+
 
 ### In web
 
 ```html
-<script src="https://unpkg.com/nanocurrency-web@1.2.0" type="text/javascript"></script>
+<script src="https://unpkg.com/nanocurrency-web@1.2.1" type="text/javascript"></script>
 <script type="text/javascript">
     NanocurrencyWeb.wallet.generate(...);
 </script>
index 58d294e74c359ed9aa3b7cbe374f5638fe974440..0e16249c4080ae1ac840402667f17317089c8cfe 100644 (file)
--- a/index.ts
+++ b/index.ts
@@ -1,14 +1,18 @@
+import BigNumber from 'bignumber.js'
+
 import AddressGenerator from './lib/address-generator'
 import AddressImporter, { Account, Wallet } from './lib/address-importer'
-import BlockSigner, { SendBlock, ReceiveBlock, RepresentativeBlock, SignedBlock } from './lib/block-signer'
-import BigNumber from 'bignumber.js'
+import BlockSigner, { ReceiveBlock, RepresentativeBlock, SendBlock, SignedBlock } from './lib/block-signer'
+import NanoAddress from './lib/nano-address'
 import NanoConverter from './lib/nano-converter'
 import Signer from './lib/signer'
 import Convert from './lib/util/convert'
 
+const nanoAddress = new NanoAddress()
 const generator = new AddressGenerator()
 const importer = new AddressImporter()
-const signer = new Signer();
+const signer = new Signer()
+
 const wallet = {
 
        /**
@@ -74,17 +78,17 @@ const wallet = {
 
        /**
         * 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);
@@ -214,7 +218,7 @@ const tools = {
 
        /**
         * Sign any strings with the user's private key
-        * 
+        *
         * @param {string} privateKey The private key to sign with
         * @param {...string} input Data to sign
         */
@@ -223,6 +227,24 @@ const tools = {
                return signer.sign(privateKey, ...data);
        },
 
+       /**
+        * Validate a Nano address
+        *
+        * @param {string} input The address to validate
+        */
+       validateAddress: (input: string): boolean => {
+               return nanoAddress.validateNanoAddress(input);
+       },
+
+       /**
+        * Validate mnemonic words
+        *
+        * @param {string} input The address to validate
+        */
+       validateMnemonic: (input: string): boolean => {
+               return importer.validateMnemonic(input);
+       },
+
 }
 
 export {
index 291538d66a671adc2b5c8cdcc0033213cfbdf261..2a3a1f1216ab2aa62985a9923d5b5dd6fd51fd2d 100644 (file)
@@ -9,8 +9,8 @@ export default class AddressImporter {
 
        /**
         * Import a wallet using a mnemonic phrase
-        * 
-        * @param {string} mnemonic - The mnemonic words to import the wallet from 
+        *
+        * @param {string} mnemonic - The mnemonic words to import the wallet from
         * @param {string} [seedPassword] - (Optional) The password to use to secure the mnemonic
         * @returns {Wallet} - The wallet derived from the mnemonic phrase
         */
@@ -24,10 +24,20 @@ export default class AddressImporter {
                return this.nano(seed, 0, 0, mnemonic)
        }
 
+       /**
+        * Validate mnemonic words
+        *
+        * @param mnemonic {string} mnemonic - The mnemonic words to validate
+        */
+       validateMnemonic(mnemonic: string): boolean {
+               const bip39 = new Bip39Mnemonic()
+               return bip39.validateMnemonic(mnemonic);
+       }
+
        /**
         * Import a wallet using a seed
-        * 
-        * @param {string} seed - The seed to import the wallet from 
+        *
+        * @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 mnemonic phrase
@@ -42,11 +52,11 @@ 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
@@ -82,7 +92,7 @@ export default class AddressImporter {
 
        /**
         * Derives the private keys
-        * 
+        *
         * @param {string} seed - The seed to use for private key derivation
         * @param {number} from - The start index of private keys to derive from
         * @param {number} to - The end index of private keys to derive to
index 6adf53319e9a8833c878c32cb0e6a325290857b5..ae1053ea1f2f3b958ad6a38a08b585190f954ad5 100644 (file)
@@ -1,21 +1,21 @@
+//@ts-ignore
+import { PBKDF2, SHA256, algo, enc, lib } from 'crypto-js'
+
 import Convert from './util/convert'
 import Util from './util/util'
 import words from './words'
 
-//@ts-ignore
-import { algo, enc, lib, PBKDF2, SHA256 } from 'crypto-js'
-
 export default class Bip39Mnemonic {
 
        password: string
 
-       constructor(password: string) {
+       constructor(password?: string) {
                this.password = password
        }
 
        /**
         * Creates a new wallet
-        * 
+        *
         * @param {string} [entropy] - (Optional) the entropy to use instead of generating
         * @returns {MnemonicSeed} The mnemonic phrase and a seed derived from the (generated) entropy
         */
@@ -53,7 +53,7 @@ export default class Bip39Mnemonic {
 
        /**
         * Validates a mnemonic phrase
-        * 
+        *
         * @param {string} mnemonic - The mnemonic phrase to validate
         * @returns {boolean} Is the mnemonic phrase valid
         */
@@ -84,7 +84,7 @@ export default class Bip39Mnemonic {
                const newChecksum = this.calculateChecksum(entropyHex)
                const inputChecksum = Convert.binaryToHexString(checksumBits)
 
-               if (newChecksum != inputChecksum) {
+               if (parseInt(newChecksum, 16) != parseInt(inputChecksum, 16)) {
                        return false
                }
 
index 805738016c6266648fd731ee80257a3cff5ec2ed..42d282018607142f76c55811744b5dd0455d8672 100644 (file)
@@ -1,8 +1,8 @@
-import Convert from './util/convert'
-
 //@ts-ignore
 import { blake2b } from 'blakejs'
 
+import Convert from './util/convert'
+
 export default class NanoAddress {
 
        readonly alphabet = '13456789abcdefghijkmnopqrstuwxyz'
@@ -82,43 +82,29 @@ export default class NanoAddress {
         * @param {string} address Nano address
         */
        validateNanoAddress = (address: string): boolean => {
-               /** Ensure the address is provided */
                if (address === undefined) {
                        throw Error('Address must be defined.')
                }
 
-               /** Ensure the address is a string */
                if (typeof address !== 'string') {
                        throw TypeError('Address must be a string.')
                }
 
-               /** The array of allowed prefixes */
                const allowedPrefixes: string[] = ['nano', 'xrb']
-
-               /** The regex pattern for validating the address */
                const pattern = new RegExp(
                        `^(${allowedPrefixes.join('|')})_[13]{1}[13456789abcdefghijkmnopqrstuwxyz]{59}$`,
                )
 
-               /** Validate the syntax of the address */
-               if (!pattern.test(address)) return false
+               if (!pattern.test(address)) {
+                       return false
+               }
 
-               /** The expected checksum as a base32-encoded string */
                const expectedChecksum = address.slice(-8)
-
-               /** The public key as a base32-encoded string */
                const publicKey = address.slice(address.indexOf('_') + 1, -8)
-
-               /** The public key as an array buffer */
                const publicKeyBuffer = this.decodeNanoBase32(publicKey)
-
-               /** The actual checksum as an array buffer */
                const actualChecksumBuffer = blake2b(publicKeyBuffer, null, 5).reverse()
-
-               /** The actual checksum as a base32-encoded string */
                const actualChecksum = this.encodeNanoBase32(actualChecksumBuffer)
 
-               /** Validate the provided checksum against the derived checksum */
                return expectedChecksum === actualChecksum
        }
 
index 72a9fbbc4cbe3ba3b78744f9d5c6ea32cf834a0d..363a6059d539b4f2f1e5ace1410b90c068e85780 100644 (file)
@@ -1,6 +1,6 @@
 {
        "name": "nanocurrency-web",
-       "version": "1.2.0",
+       "version": "1.2.1",
        "lockfileVersion": 1,
        "requires": true,
        "dependencies": {
index c3b1482e0885db21249d9653d6295e2524b490fe..3bffa75f5995da571600259ce0998dad61af78a6 100644 (file)
@@ -1,6 +1,6 @@
 {
        "name": "nanocurrency-web",
-       "version": "1.2.0",
+       "version": "1.2.1",
        "description": "Toolkit for Nano cryptocurrency client side offline integrations",
        "author": "Miro Metsänheimo <miro@metsanheimo.fi>",
        "license": "MIT",
index 865828028a494ec2d8e55eb02035030fbf355bd3..3edf7bf4255a868474d261bb625107bc07bfc7de 100644 (file)
@@ -57,7 +57,7 @@ describe('generate wallet test', () => {
 })
 
 // Test vectors from https://docs.nano.org/integration-guides/key-management/
-describe('import wallet with official test vectors test', () => {
+describe('import wallet with test vectors test', () => {
 
        it('should successfully import a wallet with the official Nano test vectors mnemonic', () => {
                const result = wallet.fromMnemonic(
@@ -73,6 +73,10 @@ describe('import wallet with official test vectors test', () => {
                expect(result.accounts[0].address).to.equal('nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d')
        })
 
+       it('should successfully import a wallet with the checksum starting with a zero', () => {
+               wallet.fromMnemonic('food define cancel major spoon trash cigar basic aim bless wolf win ability seek paddle bench seed century group they mercy address monkey cake')
+       })
+
        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')