Browse Source

Node use libsodium bindings (optional) (#32)

* Node cli improvements

* Extra cli options

* Allow withCase from UI

* Text updates
Jaco Greeff 6 years ago
parent
commit
52df94d58c

+ 15 - 0
flow-typed/sodium.js

@@ -0,0 +1,15 @@
+// @flow
+
+declare module 'sodium' {
+  declare module.exports: {
+    Key: {
+      Sign: {
+        fromSeed (seed: Buffer): {
+          getPublicKey (): {
+            baseBuffer: Buffer
+          }
+        }
+      }
+    }
+  }
+}

+ 1 - 1
package.json

@@ -10,7 +10,7 @@
     "build:apps": "cd packages/apps && react-scripts build",
     "check": "eslint packages && flow check",
     "deploy:ghpages": "gh-pages -d packages/apps/build",
-    "generator": "node packages/app-vanitygen/build/generator/cli.js",
+    "generator": "polkadot-dev-build-babel && node packages/app-vanitygen/build/generator/cli.js",
     "start": "cd packages/apps && react-scripts start",
     "test": "echo 'no tests executed'"
   },

+ 3 - 0
packages/app-vanitygen/package.json

@@ -14,5 +14,8 @@
     "@polkadot/ui-react-app": "^0.2.1",
     "chalk": "^2.4.1",
     "yargs": "10.1.2"
+  },
+  "optionalDependencies": {
+    "sodium": "^2.0.1"
   }
 }

+ 16 - 8
packages/app-vanitygen/src/generator/cli.js

@@ -8,27 +8,34 @@ const chalk = require('chalk');
 const u8aToHex = require('@polkadot/util/u8a/toHex');
 
 const generator = require('./index.js');
+const { pkFromSeed } = require('./sodium');
 
-const { match } = yargs
+const { match, withCase } = yargs
   .option('match', {
-    alias: 'm',
     default: 'EEEEE'
   })
+  .option('withCase', {
+    default: true
+  })
   .argv;
 
 const INDICATORS = ['|', '/', '-', '\\'];
+const NUMBER_REGEX = /(\d+?)(?=(\d{3})+(?!\d)|$)/g;
 
 const options = {
   match,
-  runs: 100
+  runs: 50,
+  withCase
 };
 const startAt = Date.now();
 let best = { count: -1 };
 let total = 0;
 let indicator = -1;
 
+console.log(options);
+
 function showProgress () {
-  const elapsed = Date.now() - startAt;
+  const elapsed = (Date.now() - startAt) / 1000;
 
   indicator++;
 
@@ -36,7 +43,7 @@ function showProgress () {
     indicator = 0;
   }
 
-  process.stdout.write(`\r[${INDICATORS[indicator]}] ${total} keys in ${(elapsed / 1000).toFixed(2)}s, ${(elapsed / total).toFixed(3)}ms/key`);
+  process.stdout.write(`\r[${INDICATORS[indicator]}] ${total.toString().match(NUMBER_REGEX).join(',')} keys in ${(elapsed).toFixed(2)}s (${(total / elapsed).toFixed(0)} keys/s)`);
 }
 
 function showBest () {
@@ -46,7 +53,7 @@ function showBest () {
 }
 
 while (true) {
-  const nextBest = generator(options).found.reduce((best, match) => {
+  const nextBest = generator(options, pkFromSeed).found.reduce((best, match) => {
     if ((match.count > best.count) || ((match.count === best.count) && (match.offset < best.offset))) {
       return match;
     }
@@ -59,7 +66,8 @@ while (true) {
   if (nextBest.address !== best.address) {
     best = nextBest;
     showBest();
+    showProgress();
+  } else if ((total % 1000) === 0) {
+    showProgress();
   }
-
-  showProgress();
 }

+ 7 - 6
packages/app-vanitygen/src/generator/generate.js

@@ -3,19 +3,20 @@
 // of the ISC license. See the LICENSE file for details.
 // @flow
 
-import type { Generator$Match, Generator$Options } from './types';
+import type { Generator$PkFromSeed, Generator$Match, Generator$Options } from './types';
 
-const keypairFromSeed = require('@polkadot/util-crypto/nacl/keypair/fromSeed');
 const randomBytes = require('@polkadot/util-crypto/random/asU8a');
 const addressEncode = require('@polkadot/util-keyring/address/encode');
+const pairFromSeed = require('@polkadot/util-crypto/nacl/keypair/fromSeed');
 
 const calculate = require('./calculate');
 
-module.exports = function generator (test: Array<string>, options: Generator$Options): Generator$Match {
+const tweetPkFromSeed = (seed: Uint8Array): Uint8Array =>
+  pairFromSeed(seed).publicKey;
+
+module.exports = function generator (test: Array<string>, options: Generator$Options, pkFromSeed?: Generator$PkFromSeed = tweetPkFromSeed): Generator$Match {
   const seed = randomBytes();
-  const address = addressEncode(
-    keypairFromSeed(seed).publicKey
-  );
+  const address = addressEncode(pkFromSeed(seed));
   const { count, offset } = calculate(test, address, options);
 
   return {

+ 3 - 3
packages/app-vanitygen/src/generator/index.js

@@ -3,11 +3,11 @@
 // of the ISC license. See the LICENSE file for details.
 // @flow
 
-import type { Generator$Matches, Generator$Result, Generator$Options } from './types';
+import type { Generator$PkFromSeed, Generator$Matches, Generator$Result, Generator$Options } from './types';
 
 const generate = require('./generate');
 
-module.exports = function generator (options: Generator$Options): Generator$Result {
+module.exports = function generator (options: Generator$Options, pkFromSeed?: Generator$PkFromSeed): Generator$Result {
   const { match, runs = 10, withCase = false } = options;
   const test = withCase
     ? match.split('')
@@ -16,7 +16,7 @@ module.exports = function generator (options: Generator$Options): Generator$Resu
   const found: Generator$Matches = [];
 
   while (found.length !== runs) {
-    found.push(generate(test, options));
+    found.push(generate(test, options, pkFromSeed));
   }
 
   return {

+ 29 - 0
packages/app-vanitygen/src/generator/sodium.js

@@ -0,0 +1,29 @@
+// Copyright 2017-2018 Jaco Greeff
+// This software may be modified and distributed under the terms
+// of the ISC license. See the LICENSE file for details.
+// @flow
+
+const bufferToU8a = require('@polkadot/util/buffer/toU8a');
+const u8aToBuffer = require('@polkadot/util/u8a/toBuffer');
+
+let pkFromSeed = void 0;
+
+try {
+  const sodium = require('sodium');
+
+  pkFromSeed = (seed: Uint8Array): Uint8Array =>
+    bufferToU8a(
+      sodium.Key.Sign
+        .fromSeed(u8aToBuffer(seed))
+        .getPublicKey()
+        .baseBuffer
+    );
+} catch (error) {
+  // oops, nothing
+}
+
+console.log(`Using NaCl bindings from ${!pkFromSeed ? 'tweet-nacl' : 'node-sodium'}`);
+
+module.exports = {
+  pkFromSeed
+};

+ 2 - 0
packages/app-vanitygen/src/generator/types.js

@@ -26,3 +26,5 @@ export type Generator$Result = {
   elapsed: number,
   found: Generator$Matches
 }
+
+export type Generator$PkFromSeed = (seed: Uint8Array) => Uint8Array;

+ 29 - 13
packages/app-vanitygen/src/index.js

@@ -12,6 +12,7 @@ import React from 'react';
 import Button from 'semantic-ui-react/dist/es/elements/Button';
 import Input from 'semantic-ui-react/dist/es/elements/Input';
 import Label from 'semantic-ui-react/dist/es/elements/Label';
+import Dropdown from 'semantic-ui-react/dist/es/modules/Dropdown';
 
 import Match from './Match';
 import generator from './generator';
@@ -28,11 +29,16 @@ type State = {
   keyTime: 0,
   match: string,
   matches: Generator$Matches,
-  startAt: number
+  startAt: number,
+  withCase: boolean
 }
 
-const DEFAULT_MATCH = 'S?me';
+const DEFAULT_MATCH = 'Some?';
 const MATCH_REGEX = /[1-9A-Za-z?]*$/;
+const BOOL_OPTIONS = [
+  { text: 'No', value: false },
+  { text: 'Yes', value: true }
+];
 
 class App extends React.PureComponent<Props, State> {
   results: Array<Generator$Result>;
@@ -50,13 +56,14 @@ class App extends React.PureComponent<Props, State> {
       keyTime: 0,
       match: DEFAULT_MATCH,
       matches: [],
-      startAt: 0
+      startAt: 0,
+      withCase: true
     };
   }
 
   render (): React$Node {
     const { className, style, t } = this.props;
-    const { elapsed, isMatchValid, isRunning, match, matches, keyCount } = this.state;
+    const { elapsed, isMatchValid, isRunning, match, matches, keyCount, withCase } = this.state;
 
     return (
       <div
@@ -66,7 +73,7 @@ class App extends React.PureComponent<Props, State> {
         <div className='ui--form'>
           <div className='medium'>
             <Label>{t('vanity.matching', {
-              defaultValue: 'generate address containing characters'
+              defaultValue: 'generate address containing (? wildcard)'
             })}</Label>
             <Input
               disabled={isRunning}
@@ -77,13 +84,14 @@ class App extends React.PureComponent<Props, State> {
           </div>
           <div className='small'>
             <Label>{t('vanity.case', {
-              defaultValue: 'case sensitivity'
+              defaultValue: 'case sensitive match'
             })}</Label>
-            <div className='ui dropdown selection disabled'>
-              {t('vanity.case.off', {
-                defaultValue: 'No'
-              })}
-            </div>
+            <Dropdown
+              selection
+              options={BOOL_OPTIONS}
+              onChange={this.onChangeCase}
+              value={withCase}
+            />
           </div>
           <div className='small'>
             <Label>{t('vanity.offset', {
@@ -115,7 +123,7 @@ class App extends React.PureComponent<Props, State> {
             ? (
               <div className='vanity--App-stats'>
                 {t('vanity.stats', {
-                  defaultValue: 'Generated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
+                  defaultValue: 'Evaluated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
                   replace: {
                     avg: (elapsed / keyCount).toFixed(3),
                     count: keyCount,
@@ -189,7 +197,8 @@ class App extends React.PureComponent<Props, State> {
       this.results.push(
         generator({
           match: this.state.match,
-          runs: 10
+          runs: 10,
+          withCase: this.state.withCase
         })
       );
 
@@ -197,6 +206,13 @@ class App extends React.PureComponent<Props, State> {
     }, 0);
   }
 
+  // eslint-disable-next-line no-unused-vars
+  onChangeCase = (event: SyntheticEvent<*>, { value }): void => {
+    this.setState({
+      withCase: value
+    });
+  }
+
   // eslint-disable-next-line no-unused-vars
   onChangeMatch = (event: SyntheticEvent<*>, { value }): void => {
     const isMatchValid = MATCH_REGEX.test(value) && value.length < 31;

+ 7 - 1
yarn.lock

@@ -7047,7 +7047,7 @@ mz@^2.6.0:
     object-assign "^4.0.1"
     thenify-all "^1.0.0"
 
-nan@^2.0.7, nan@^2.3.0, nan@^2.3.3, nan@^2.9.2:
+nan@^2.0.7, nan@^2.3.0, nan@^2.3.3, nan@^2.8.0, nan@^2.9.2:
   version "2.10.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
 
@@ -9258,6 +9258,12 @@ sockjs@0.3.19:
     faye-websocket "^0.10.0"
     uuid "^3.0.1"
 
+sodium@^2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/sodium/-/sodium-2.0.3.tgz#4f489e9a3be75395d168a0341dde8b401432ddf6"
+  dependencies:
+    nan "^2.8.0"
+
 sort-keys@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"