crypti-cli
Version:
Crypti DApps Command Line Interface
803 lines (698 loc) • 21.4 kB
JavaScript
var inquirer = require("inquirer");
var program = require('commander');
var accountHelper = require('./helpers/account.js');
var blockHelper = require('./helpers/block.js');
var dappHelper = require('./helpers/dapp.js');
var gift = require('gift');
var fs = require('fs');
var path = require('path');
var rmdir = require('rmdir');
var cryptoLib = require('./lib/crypto.js');
var npm = require('npm');
var request = require('request');
var toolkit = "git@github.com:crypti/DAppToolkit.git";
program.version('1.0.9');
program
.command("dapps")
.description("manage your dapps")
.option("-a, --add", "add new dapp")
.option("-c, --change", "change dapp genesis block")
.option("-d, --deposit", "deposit funds to dapp")
.option("-w, --withdrawal", "withdraw funds from dapp")
.action(function (options) {
if (options.add) {
inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Existing blockchain.db file will be replaced, are you sure?",
default: false
}
], function (result) {
if (result.confirmed) {
inquirer.prompt([
{
type: "password",
name: "secret",
message: "Enter secret of your testnet account",
validate: function (value) {
var done = this.async();
if (value.length == 0) {
done("Secret is too short, minimum is 1 character");
return;
}
if (value.length > 100) {
done("Secret is too long, maximum is 100 characters");
return;
}
done(true);
}
}
], function (result) {
var account = accountHelper.account(result.secret);
inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Update existing genesis block? (or create a new one)",
default: false
}
], function (result) {
var genesisBlock = null;
var newGenesisBlock = !result.confirmed;
if (!newGenesisBlock) {
try {
var genesisBlock = JSON.parse(fs.readFileSync(path.join('.', 'genesisBlock.json'), 'utf8'));
} catch (e) {
console.log("Can't read genesisBlock.js: ", e.toString());
return;
}
}
inquirer.prompt([
{
type: "input",
name: "name",
message: "Enter DApp name",
required: true,
validate: function (value) {
var done = this.async();
if (value.length == 0) {
done("DApp name is too short, minimum is 1 character");
return;
}
if (value.length > 32) {
done("DApp name is too long, maximum is 32 characters");
return;
}
return done(true)
}
},
{
type: "input",
name: "description",
message: "Enter DApp description",
validate: function (value) {
var done = this.async();
if (value.length > 160) {
done("DApp description is too long, maximum is 160 characters");
return;
}
return done(true);
}
},
{
type: "input",
name: "git",
message: "Github repository",
required: true,
validate: function (value) {
var done = this.async();
if (!(/^git\@github\.com\:.+\.git$/.test(value))) {
done("Incorrect github repository link");
return;
}
return done(true);
}
}
], function (result) {
console.log("Generating unique genesis block...");
// create dapp and save to genesis block
var block, dapp, delegates;
if (newGenesisBlock) {
var r = blockHelper.new(account,
{
name: result.name,
description: result.description,
git: result.git,
type: 0,
category: 0
}
);
block = r.block;
dapp = r.dapp;
delegates = r.delegates;
} else {
try {
var r = blockHelper.from(genesisBlock, account,
{
name: result.name,
description: result.description,
git: result.git,
type: 0,
category: 0
}
);
} catch (e) {
return console.log(e);
}
block = r.block;
dapp = r.dapp;
}
inquirer.prompt([
{
type: "input",
name: "publicKeys",
message: "Enter public keys of dapp forgers - hex array, use ',' for separator",
default: account.keypair.publicKey,
validate: function (value) {
var done = this.async();
var publicKeys = value.split(',');
if (publicKeys.length == 0) {
done('DApp requires at least 1 public key');
return;
}
for (var i in publicKeys) {
try {
var b = new Buffer(publicKeys[i], 'hex');
if (b.length != 32) {
done('Incorrect public key: ' + publicKeys[i]);
return;
}
} catch (e) {
done('Incorrect hex for public key: ' + publicKeys[i]);
return;
}
}
done(true);
}
}
], function (result) {
console.log("Creating DApp genesis block");
var dappBlock = dappHelper.new(account, block, result.publicKeys.split(','));
console.log("Fetching Crypti DApp Toolkit");
var dappsPath = path.join('.', 'dapps');
fs.exists(dappsPath, function (exists) {
if (!exists) {
fs.mkdirSync(dappsPath);
}
var dappPath = path.join(dappsPath, dapp.id);
gift.clone(toolkit, dappPath, function (err, repo) {
if (err) {
return console.log(err.toString());
}
rmdir(path.join(dappPath, ".git"), function (err) {
if (err) {
return console.log(err.toString());
}
console.log("Connecting local repository with remote repository");
gift.init(dappPath, function (err, repo) {
if (err) {
return console.log(err.toString());
}
repo.remote_add('origin', dapp.asset.dapp.git, function (err, repo) {
if (err) {
return console.log(err.toString());
}
var bcFile = path.join('.', 'blockchain.db');
var exists = fs.existsSync(bcFile);
if (exists) {
fs.unlinkSync(bcFile);
}
// load npm config
var packageJson = path.join(dappPath, "package.json");
var config = null;
try {
config = JSON.parse(fs.readFileSync(packageJson));
} catch (e) {
return setImmediate(cb, "Incorrect package.json file for " + dApp.transactionId + " DApp");
}
npm.load(config, function (err) {
npm.root = path.join(dappPath, "node_modules");
npm.prefix = dappPath;
npm.commands.install(function (err, data) {
if (err) {
return console.log(err);
} else {
console.log("Saving genesis block");
var genesisBlockJson = JSON.stringify(block, null, 4);
try {
fs.writeFileSync(path.join('.', 'genesisBlock.json'), genesisBlockJson, "utf8");
} catch (e) {
return console.log(err);
}
var dappGenesisBlockJson = JSON.stringify(dappBlock, null, 4);
try {
fs.writeFileSync(path.join(dappPath, 'genesis.json'), dappGenesisBlockJson, "utf8");
} catch (e) {
return console.log(err);
}
console.log("Updating config");
try {
var config = JSON.parse(fs.readFileSync(path.join('.', 'config.json'), 'utf8'));
} catch (e) {
return console.log(e);
}
if (newGenesisBlock) {
config.forging = config.forging || {};
config.forging.secret = delegates.map(function (d) {
return d.secret;
});
}
inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Add dapp to autolaunch"
}
], function (result) {
if (result.confirmed) {
config.dapp = config.dapp || {};
config.dapp.autoexec = config.dapp.autoexec || [];
config.dapp.autoexec.push({
params: [
account.secret,
"modules.full.json"
],
dappid: dapp.id
})
}
fs.writeFile(path.join('.', 'config.json'), JSON.stringify(config, null, 2), function (err) {
if (err) {
console.log(err);
} else {
console.log("Done (DApp id is " + dapp.id + ")");
}
});
});
}
});
});
});
});
});
});
});
});
});
});
});
}
});
} else if (options.change) {
inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Existing blockchain.db file will be replaced, are you sure?",
default: false
}
], function (result) {
if (result.confirmed) {
inquirer.prompt([
{
type: "password",
name: "secret",
message: "Enter secret of your testnet account",
validate: function (value) {
var done = this.async();
if (value.length == 0) {
done("Secret is too short, minimum is 1 character");
return;
}
if (value.length > 100) {
done("Secret is too long, maximum is 100 characters");
return;
}
done(true);
}
}
], function (result) {
var account = accountHelper.account(result.secret);
inquirer.prompt([
{
type: "input",
name: "dappId",
message: "Enter DApp id (folder name of dapp)",
required: true
},
], function (result) {
var dappId = result.dappId,
publicKeys = [];
var dappPath = path.join('.', 'dapps', dappId);
var dappGenesis = require(path.join(dappPath, 'genesis.json'));
inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Continue with exists forgers public keys",
required: true,
}], function (result) {
if (result.confirmed) {
publicKeys = dappGenesis.delegates;
}
inquirer.prompt([
{
type: "input",
name: "publicKeys",
message: "Enter public keys of dapp forgers - hex array, use ',' for separator",
default: account.keypair.publicKey,
validate: function (value) {
var done = this.async();
var publicKeys = value.split(',');
if (publicKeys.length == 0) {
done('DApp requires at least 1 public key');
return;
}
for (var i in publicKeys) {
try {
var b = new Buffer(publicKeys[i], 'hex');
if (b.length != 32) {
done('Incorrect public key: ' + publicKeys[i]);
return;
}
} catch (e) {
done('Incorrect hex for public key: ' + publicKeys[i]);
return;
}
}
done(true);
}
}
], function (result) {
publicKeys = publicKeys.concat(result.publicKeys.split(','));
// generate new genesis block
console.log("Creating DApp genesis block");
var bcFile = path.join('.', 'blockchain.db');
var exists = fs.existsSync(bcFile);
if (exists) {
fs.unlinkSync(bcFile);
}
var dappBlock = dappHelper.new(account, block, result.publicKeys.split(','));
var dappGenesisBlockJson = JSON.stringify(dappBlock, null, 4);
try {
fs.writeFileSync(path.join(dappPath, 'genesis.json'), dappGenesisBlockJson, "utf8");
} catch (e) {
return console.log(err);
}
console.log("Done");
});
});
});
});
}
});
} else if (options.deposit) {
inquirer.prompt([
{
type: "password",
name: "secret",
message: "Enter secret",
validate: function (value) {
return value.length > 0 && value.length < 100;
},
required: true
},
{
type: "input",
name: "amount",
message: "Enter amount",
validate: function (value) {
return !isNaN(parseInt(value));
},
required: true
},
{
type: "input",
name: "dappId",
message: "DApp Id",
required: true
},
{
type: "input",
name: "secondSecret",
message: "Enter secondary secret (if defined)",
validate: function (value) {
return value.length < 100;
},
required: false
}
], function (result) {
// body
var body = {
secret: result.secret,
dappId: result.dappId,
amount: parseInt(result.amount)
};
if (result.secondSecret && result.secondSecret.length > 0) {
body.secondSecret = result.secondSecret;
}
inquirer.prompt([
{
type: "input",
name: "host",
message: "Host and port",
default: "localhost:7040",
required: true
}
], function (result) {
request({
url: "http://" + result.host + "/api/dapps/transaction",
method: "put",
json: true,
body: body
}, function (err, resp, body) {
console.log(err, body);
if (err) {
return console.log(err.toString());
}
if (body.success) {
console.log(body.transactionId);
return;
} else {
return console.log(body.error);
}
});
});
});
} else if (options.withdrawal) {
inquirer.prompt([
{
type: "password",
name: "secret",
message: "Enter secret",
validate: function (value) {
return value.length > 0 && value.length < 100;
},
required: true
},
{
type: "input",
name: "amount",
message: "Amount",
validate: function (value) {
return !isNaN(parseInt(value));
},
required: true
},
{
type: "input",
name: "dappId",
message: "Enter DApp id",
validate: function (value) {
var isAddress = /^[0-9]$/g;
return isAddress.test(value);
},
required: true
}], function (result) {
var body = {
secret: result.secret,
amount: result.amount
};
request({
url: "http://localhost:7040/api/dapps/" + result.dappId + "/api/withdrawal",
method: "post",
json: true,
body: body
}, function (err, resp, body) {
if (err) {
return console.log(err.toString());
}
if (body.success) {
console.log(body.response.transactionId);
} else {
return console.log(body.error);
}
});
});
} else {
console.log("'node dapps -h' to get help");
}
});
program
.command('contract')
.description('contract operations')
.option('-a, --add', "add new contract")
.option('-d, --delete', "delete contract")
.action(function (options) {
var contractsPath = path.join('.', 'modules', 'contracts');
fs.exists(contractsPath, function (exist) {
if (exist) {
if (options.add) {
fs.readdir(contractsPath, function (err, filenames) {
if (err) {
return console.log(err);
}
inquirer.prompt([
{
type: "input",
name: "filename",
message: "Contract file name (without .js)"
}
], function (result) {
var name = result.filename,
type = filenames.length + 1,
filename = result.filename + ".js";
fs.readFile(path.join(__dirname, "contract-example.js"), "utf8", function (err, exampleContract) {
if (err) {
return console.log(err);
}
exampleContract = exampleContract.replace(/ExampleContract/g, name);
exampleContract = exampleContract.replace("//self.type = null;", "self.type = " + type);
fs.writeFile(path.join(contractsPath, filename), exampleContract, "utf8", function (err) {
if (err) {
return console.log(err);
} else {
console.log("New contract created: " + ("./contracts/" + filename));
console.log("Updating contracts list");
fs.readFile(path.join('.', 'modules.full.json'), 'utf8', function (err, text) {
if (err) {
return console.log(err);
}
try {
var modules = JSON.parse(text);
} catch (e) {
return console.log(e);
}
var contractName = "contracts/" + name;
var dappPathConfig = "./" + path.join(contractsPath, filename);
modules[contractName] = dappPathConfig;
modules = JSON.stringify(modules, false, 4);
fs.writeFile(path.join('.', 'modules.full.json'), modules, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log("Done");
});
});
}
});
});
});
});
} else if (options.delete) {
inquirer.prompt([
{
type: "input",
name: "filename",
message: "Contract file name (without .js)"
}
], function (result) {
var name = result.filename,
type = filenames.length + 1,
filename = result.filename + ".js";
// проверяем что контракт существуем
// удаляем
// удаляем из modules.full.json
var contractPath = path.join(contractsPath, filename);
fs.exists(contractPath, function (exists) {
if (exists) {
fs.unlink(contractPath, function (err) {
if (err) {
return console.log(err);
}
console.log("Contract removed");
console.log("Updating contracts list");
fs.readFile(path.join('.', 'modules.full.json'), 'utf8', function (err, text) {
if (err) {
return console.log(err);
}
try {
var modules = JSON.parse(text);
} catch (e) {
return console.log(e);
}
var name = "contracts/" + name;
delete modules[name];
modules = JSON.stringify(modules, false, 4);
fs.writeFile(path.join('.', 'modules.full.json'), modules, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log("Done");
});
});
});
} else {
return console.log("Contract not found: " + contractPath);
}
});
});
} else {
}
} else {
return console.log('./modules/contracts path not found, please change directory to your dapp folder');
}
});
});
program
.command('crypto')
.description("crypto operations")
.option('-p, --pubkey', "generate public key from secret")
.option('-g, --generate', "generate random accounts")
.action(function (options) {
if (options.pubkey) {
inquirer.prompt([
{
type: "password",
name: "secret",
message: "Enter secret of your testnet account",
validate: function (value) {
var done = this.async();
if (value.length == 0) {
done("Secret is too short, minimum is 1 character");
return;
}
if (value.length > 100) {
done("Secret is too long, maximum is 100 characters");
return;
}
done(true);
}
}
], function (result) {
var account = accountHelper.account(result.secret);
console.log("Public key: " + account.keypair.publicKey);
});
} else if (options.generate) {
inquirer.prompt([
{
type: "input",
name: "amount",
message: "Enter number of accounts to generate",
validate: function (value) {
var num = parseInt(value);
return !isNaN(num);
}
}
], function (result) {
var n = parseInt(result.amount),
accounts = [];
for (var i = 0; i < n; i++) {
var a = accountHelper.account(cryptoLib.randomString(32));
accounts.push({
address: a.address,
secret: a.secret,
publicKey: a.keypair.publicKey
});
}
console.log(accounts);
console.log("Done");
});
} else {
console.log("'node crypto -h' to get help");
}
});
if (!process.argv.slice(2).length) {
program.outputHelp();
}
program.parse(process.argv);