Fix some checks and add a CLI

This commit is contained in:
Marvin ROGER 2018-07-11 16:50:52 +02:00 committed by Marvin ROGER
commit 2a74f4e07b
8 changed files with 378 additions and 12 deletions

View file

@ -2,6 +2,7 @@ const INVALID_SEEDS = [
'',
12,
'947ee0115014a4d49a804e7fc7248e31690b80033ce7a6e3a07bdf93b2584ff',
'947ee0115014a4d49a804e7fc7248e31690b80033ce7a6e3a07bdf93b2584ffff',
'z947ee0115014a4d49a804e7fc7248e31690b80033ce7a6e3a07bdf93b2584ff',
];
@ -11,6 +12,7 @@ const INVALID_SECRET_KEYS = [
'',
12,
'3b5e95b4c4325ed5af109bfe4acde782dbab0163591d9052963723ae8e72a09',
'3b5e95b4c4325ed5af109bfe4acde782dbab0163591d9052963723ae8e72a0999',
'z3b5e95b4c4325ed5af109bfe4acde782dbab0163591d9052963723ae8e72a09',
];
@ -18,6 +20,7 @@ const INVALID_PUBLIC_KEYS = [
'',
12,
'd312f604f638adf19afac6308ecbbc5881e1b6cd6f53d382775c686bca7535b',
'd312f604f638adf19afac6308ecbbc5881e1b6cd6f53d382775c686bca7535bbb',
'zd312f604f638adf19afac6308ecbbc5881e1b6cd6f53d382775c686bca7535b',
];
@ -25,14 +28,17 @@ const INVALID_HASHES = [
'',
12,
'f7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be6',
'f7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be666',
'zf7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be6',
];
const INVALID_WORKS = ['', 12, '000000000995bc3', 'z000000000995bc3'];
const INVALID_WORKS = ['', 12, '000000000995bc3', '000000000995bc333', 'z000000000995bc3'];
const INVALID_ADDRESSES = [
'',
12,
'axrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1n4',
'xrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1n44',
'xrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1n4', // bad checksum
'zrb_1mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1nz',
'xrb_2mbj7xi6yrwcuwetzd5535pdqjea5rfpsoqo9nw4gxg8itycgntucp49i1nz',
@ -44,6 +50,7 @@ const INVALID_AMOUNTS = [
'',
12,
'bla',
'0100',
'1000000000000000000000000000000000000000',
'-1',
// a bit more than 2^128

View file

@ -22,6 +22,7 @@
},
"devDependencies": {
"@types/node": "^10.0.4",
"@types/yargs": "^11.0.0",
"bundlesize": "^0.17.0",
"cross-env": "^5.1.3",
"cross-os": "^1.2.2",
@ -42,7 +43,8 @@
"tslint-config-prettier": "^1.12.0",
"typedoc": "^0.11.1",
"typedoc-plugin-internal-external": "^1.0.10",
"typescript": "^2.8.3"
"typescript": "^2.8.3",
"yargs": "^11.0.0"
},
"files": [
"dist/"
@ -65,6 +67,9 @@
"main": "dist/nanocurrency.cjs.js",
"module": "dist/nanocurrency.esm.js",
"types": "dist/types/index.d.ts",
"bin": {
"nanocurrency": "dist/bin/index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/marvinroger/nanocurrency-js.git"

View file

@ -3,6 +3,7 @@ import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import license from 'rollup-plugin-license';
import pkg from './package.json';
const ENV = process.env.NODE_ENV;
@ -33,6 +34,14 @@ const config = [
],
plugins: [resolve(), commonjs(), typescript({ useTsconfigDeclarationDir: true })],
},
{
input: 'src/cli/index.ts',
output: { file: pkg.bin.nanocurrency, format: 'cjs', banner: '#!/usr/bin/env node' },
plugins: [resolve(), commonjs(), typescript({ useTsconfigDeclarationDir: true })],
external(id) {
return /^[\w-]+$/.test(id) || id === '../nanocurrency.cjs';
},
},
];
if (ENV === 'production') {

View file

@ -36,11 +36,7 @@ export function checkNumber(candidate: any) {
* @returns Valid
*/
export function checkAmount(amount: any) {
if (!checkString(amount) || amount === '' || amount.length > 39) return false;
for (const char of amount) {
if (char < '0' || char > '9') return false;
}
if (!checkString(amount) || !/^[1-9]{1}[0-9]{0,38}$/.test(amount)) return false;
const candidate = new BigNumber(amount);
@ -54,7 +50,7 @@ export function checkAmount(amount: any) {
* @returns Valid
*/
export function checkSeed(seed: any) {
return checkString(seed) && /[0-9a-fA-F]{64}/.test(seed);
return checkString(seed) && /^[0-9a-fA-F]{64}$/.test(seed);
}
/**
@ -96,7 +92,7 @@ export function checkAddress(address: any) {
* @returns Valid
*/
export function checkWork(work: any) {
return checkString(work) && /[0-9a-fA-F]{16}/.test(work);
return checkString(work) && /^[0-9a-fA-F]{16}$/.test(work);
}
/**
@ -106,5 +102,5 @@ export function checkWork(work: any) {
* @returns Valid
*/
export function checkSignature(signature: any) {
return checkString(signature) && /[0-9a-fA-F]{128}/.test(signature);
return checkString(signature) && /^[0-9a-fA-F]{128}$/.test(signature);
}

345
src/cli/index.ts Normal file
View file

@ -0,0 +1,345 @@
import * as yargs from 'yargs';
const nanocurrency = require('../nanocurrency.cjs');
const wrapSubcommand = (yargs: yargs.Argv) =>
yargs
.updateStrings({
'Commands:': 'item:',
})
.demandCommand(1, 'Please specify an item')
.help()
.version(false)
.wrap(null);
yargs
.usage('usage: $0 <command>')
.command('check', 'check a [seed|amount|hash|key|address|work|signature]', yargs => {
return wrapSubcommand(
yargs
.usage('usage: $0 check <item>')
.command(
'seed',
'check a seed',
yargs => {
return yargs.usage('usage: $0 check seed [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkSeed(argv.candidate);
console.log(valid);
}
)
.command(
'amount',
'check an amount',
yargs => {
return yargs.usage('usage: $0 check amount [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkAmount(argv.candidate);
console.log(valid);
}
)
.command(
'hash',
'check an hash',
yargs => {
return yargs.usage('usage: $0 check hash [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkHash(argv.candidate);
console.log(valid);
}
)
.command(
'key',
'check a public or private key',
yargs => {
return yargs.usage('usage: $0 check key [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkKey(argv.candidate);
console.log(valid);
}
)
.command(
'address',
'check an address',
yargs => {
return yargs.usage('usage: $0 check address [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkAddress(argv.candidate);
console.log(valid);
}
)
.command(
'work',
'check a work',
yargs => {
return yargs.usage('usage: $0 check work [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkWork(argv.candidate);
console.log(valid);
}
)
.command(
'signature',
'check a signature',
yargs => {
return yargs.usage('usage: $0 check signature [options]').option('candidate', {
demandOption: true,
describe: 'candidate to check',
type: 'string',
});
},
argv => {
const valid = nanocurrency.checkSignature(argv.candidate);
console.log(valid);
}
)
);
})
.command('convert', 'convert an [amount]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 convert <item>').command(
'amount',
'convert an amount',
yargs => {
return yargs
.usage('usage: $0 convert amount [options]')
.option('input', {
demandOption: true,
describe: 'input to convert',
type: 'string',
})
.option('from', {
demandOption: true,
describe: 'source unit',
type: 'string',
})
.option('to', {
demandOption: true,
describe: 'destination unit',
type: 'string',
});
},
async argv => {
const converted = await nanocurrency.convert(argv.input, {
from: argv.from,
to: argv.to,
});
console.log(converted);
}
)
);
})
.command('compute', 'compute a [work]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 compute <item>').command(
'work',
'compute a work',
yargs => {
return yargs.usage('usage: $0 compute work [options]').option('hash', {
demandOption: true,
describe: 'block hash to compute a work for',
type: 'string',
});
},
async argv => {
const work = await nanocurrency.computeWork(argv.hash);
console.log(work);
}
)
);
})
.command('sign', 'sign a [block]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 sign <item>').command(
'block',
'sign a block',
yargs => {
return yargs
.usage('usage: $0 sign block [options]')
.option('secret', {
demandOption: true,
describe: 'secret key to sign the block with',
type: 'string',
})
.option('hash', {
demandOption: true,
describe: 'hash of the block to sign',
type: 'string',
});
},
async argv => {
const signature = await nanocurrency.signBlock({
hash: argv.hash,
secretKey: argv.secret,
});
console.log(signature);
}
)
);
})
.command('verify', 'verify a [block]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 verify <item>').command(
'verify',
'verify a block',
yargs => {
return yargs
.usage('usage: $0 verify block [options]')
.option('public', {
demandOption: true,
describe: 'public key to verify the signature against',
type: 'string',
})
.option('hash', {
demandOption: true,
describe: 'hash of the block to verify',
type: 'string',
})
.option('signature', {
demandOption: true,
describe: 'signature to verify',
type: 'string',
});
},
async argv => {
const valid = await nanocurrency.verifyBlock({
hash: argv.hash,
publicKey: argv.public,
signature: argv.signature,
});
console.log(valid);
}
)
);
})
.command('validate', 'validate a [work]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 validate <item>').command(
'work',
'validate a work',
yargs => {
return yargs
.usage('usage: $0 validate work [options]')
.option('hash', {
demandOption: true,
describe: 'hash to validate the work against',
type: 'string',
})
.option('work', {
demandOption: true,
describe: 'work to validate',
type: 'string',
});
},
async argv => {
const valid = await nanocurrency.validateWork({
blockHash: argv.hash,
work: argv.work,
});
console.log(valid);
}
)
);
})
.command('generate', 'generate a [seed]', yargs => {
return wrapSubcommand(
yargs.usage('usage: $0 generate <item>').command('seed', 'generate a seed', {}, async () => {
const seed = await nanocurrency.generateSeed();
console.log(seed);
})
);
})
.command('derive', 'derive a [secret|public|address]', yargs => {
return wrapSubcommand(
yargs
.usage('usage: $0 derive <item>')
.command(
'secret',
'derive a secret key from a seed and an index',
yargs => {
return yargs
.usage('usage: $0 derive secret [options]')
.option('from', {
demandOption: true,
describe: 'seed to derive from',
type: 'string',
})
.option('index', {
demandOption: true,
default: 0,
describe: 'index to derive',
type: 'number',
});
},
argv => {
const secretKey = nanocurrency.deriveSecretKey(argv.from, argv.index);
console.log(secretKey);
}
)
.command(
'public',
'derive a public key from a secret key or an address',
yargs => {
return yargs.usage('usage: $0 derive public [options]').option('from', {
demandOption: true,
describe: 'secret key or address to derive from',
type: 'string',
});
},
argv => {
const publicKey = nanocurrency.derivePublicKey(argv.from);
console.log(publicKey);
}
)
.command(
'address',
'derive an address from a public key',
yargs => {
return yargs.usage('usage: $0 derive address [options]').option('from', {
demandOption: true,
describe: 'public key to derive from',
type: 'string',
});
},
argv => {
const address = nanocurrency.deriveAddress(argv.from);
console.log(address);
}
)
);
})
.demandCommand(1, 'Please specify a command')
.strict()
.help()
.epilogue('for more information, find the sources at http://git.io/nanocurrency-js')
.wrap(null).argv;

View file

@ -70,7 +70,7 @@ export function convert(value: string, params: ConvertParams) {
const valueNotValid = new Error('Value is not valid');
if (!checkString) throw valueNotValid;
if (params.from === 'hex') {
if (!/[0-9a-fA-F]{32}/.test(value)) throw valueNotValid;
if (!/^[0-9a-fA-F]{32}$/.test(value)) throw valueNotValid;
} else {
if (!checkNumber(value)) throw valueNotValid;
}

View file

@ -18,7 +18,7 @@ export interface ParseAddressResult {
/** @hidden */
export function parseAddress(address: any): ParseAddressResult {
const invalid = { valid: false, publicKeyBytes: null };
if (!checkString(address) || !/(xrb_|nano_)[13][0-9a-km-uw-z]{59}/.test(address)) {
if (!checkString(address) || !/^(xrb_|nano_)[13][0-9a-km-uw-z]{59}$/.test(address)) {
return invalid;
}

View file

@ -177,6 +177,10 @@
"@types/glob" "*"
"@types/node" "*"
"@types/yargs@^11.0.0":
version "11.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-11.0.0.tgz#124b9ed9c65b7091cc36da59ae12cbd47d8745ea"
JSONStream@^1.0.4:
version "1.3.2"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"