1 |
|
2 | var inquirer = require("inquirer");
|
3 | var gift = require("gift");
|
4 | var fs = require("fs");
|
5 | var async = require('async');
|
6 | var path = require("path");
|
7 | var rmdir = require("rmdir");
|
8 | var npm = require("npm");
|
9 | var request = require("request");
|
10 | var valid_url = require("valid-url");
|
11 | var fsExtra = require('fs-extra');
|
12 | var AschJS = require('asch-js');
|
13 | var accountHelper = require("../helpers/account.js");
|
14 | var blockHelper = require("../helpers/block.js");
|
15 | var dappHelper = require("../helpers/dapp.js");
|
16 | var Api = require("../helpers/api.js");
|
17 |
|
18 | var templatePath = path.join(__dirname, "..", "template");
|
19 |
|
20 | var dappCategories = [
|
21 | "Common",
|
22 | "Business",
|
23 | "Catalogs",
|
24 | "Education",
|
25 | "Entertainment",
|
26 | "Multimedia",
|
27 | "Networking",
|
28 | "Utilities",
|
29 | "Games"
|
30 | ];
|
31 | function addDapp() {
|
32 | var account;
|
33 | var secondSecret;
|
34 | var dappParams;
|
35 | var dappTrs;
|
36 | var dappBlock;
|
37 | var dappsPath = path.join(".", "dapps");
|
38 | var dappPath;
|
39 | async.series([
|
40 | function(next) {
|
41 | inquirer.prompt([
|
42 | {
|
43 | type: "password",
|
44 | name: "secret",
|
45 | message: "Enter secret of your testnet account",
|
46 | validate: function (value) {
|
47 | var done = this.async();
|
48 |
|
49 | if (!accountHelper.isValidSecret(value)) {
|
50 | done("Secret is not validated by BIP39");
|
51 | return;
|
52 | }
|
53 |
|
54 | done(true);
|
55 | }
|
56 | },
|
57 | {
|
58 | type: "password",
|
59 | name: "secondSecret",
|
60 | message: "Enter second secret of your testnet account if you have"
|
61 | }
|
62 | ], function (result) {
|
63 | account = accountHelper.account(result.secret);
|
64 | secondSecret = result.secondSecret;
|
65 | next();
|
66 | })
|
67 | },
|
68 | function(next) {
|
69 | inquirer.prompt([
|
70 | {
|
71 | type: "input",
|
72 | name: "name",
|
73 | message: "Enter DApp name",
|
74 | required: true,
|
75 | validate: function (value) {
|
76 | var done = this.async();
|
77 |
|
78 | if (value.length == 0) {
|
79 | done("DApp name is too short, minimum is 1 character");
|
80 | return;
|
81 | }
|
82 |
|
83 | if (value.length > 32) {
|
84 | done("DApp name is too long, maximum is 32 characters");
|
85 | return;
|
86 | }
|
87 |
|
88 | return done(true)
|
89 | }
|
90 | },
|
91 | {
|
92 | type: "input",
|
93 | name: "description",
|
94 | message: "Enter DApp description",
|
95 | validate: function (value) {
|
96 | var done = this.async();
|
97 |
|
98 | if (value.length > 160) {
|
99 | done("DApp description is too long, maximum is 160 characters");
|
100 | return;
|
101 | }
|
102 |
|
103 | return done(true);
|
104 | }
|
105 | },
|
106 | {
|
107 | type: "input",
|
108 | name: "tags",
|
109 | message: "Enter DApp tags",
|
110 | validate: function (value) {
|
111 | var done = this.async();
|
112 |
|
113 | if (value.length > 160) {
|
114 | done("DApp tags is too long, maximum is 160 characters");
|
115 | return;
|
116 | }
|
117 |
|
118 | return done(true);
|
119 | }
|
120 | },
|
121 | {
|
122 | type: "rawlist",
|
123 | name: "category",
|
124 | required: true,
|
125 | message: "Choose DApp category",
|
126 | choices: dappCategories
|
127 | },
|
128 | {
|
129 | type: "input",
|
130 | name: "link",
|
131 | message: "Enter DApp link",
|
132 | required: true,
|
133 | validate: function (value) {
|
134 | var done = this.async();
|
135 |
|
136 | if (!valid_url.isUri(value)) {
|
137 | done("Invalid DApp link, must be a valid url");
|
138 | return;
|
139 | }
|
140 | if (value.indexOf(".zip") != value.length - 4) {
|
141 | done("Invalid DApp link, does not link to zip file");
|
142 | return;
|
143 | }
|
144 | if (value.length > 160) {
|
145 | return done("DApp link is too long, maximum is 160 characters");
|
146 | }
|
147 |
|
148 | return done(true);
|
149 | }
|
150 | },
|
151 | {
|
152 | type: "input",
|
153 | name: "icon",
|
154 | message: "Enter DApp icon url",
|
155 | validate: function (value) {
|
156 | var done = this.async();
|
157 |
|
158 | if (!valid_url.isUri(value)) {
|
159 | return done("Invalid DApp icon, must be a valid url");
|
160 | }
|
161 | var extname = path.extname(value);
|
162 | if (['.png', '.jpg', '.jpeg'].indexOf(extname) == -1) {
|
163 | return done("Invalid DApp icon file type");
|
164 | }
|
165 | if (value.length > 160) {
|
166 | return done("DApp icon url is too long, maximum is 160 characters");
|
167 | }
|
168 |
|
169 | return done(true);
|
170 | }
|
171 | }
|
172 | ], function (result) {
|
173 | if (!result.name || !result.link || !result.category) {
|
174 | return next(new Error('invalid dapp params'));
|
175 | }
|
176 | dappParams = {
|
177 | name: result.name,
|
178 | link: result.link,
|
179 | category: dappCategories.indexOf(result.category) + 1,
|
180 | description: result.description || "",
|
181 | tags: result.tags || "",
|
182 | icon: result.icon || "",
|
183 | type: 0
|
184 | };
|
185 | dappTrs = AschJS.dapp.createDapp(account.secret, secondSecret, dappParams);
|
186 | console.log("Generate dapp transaction", dappTrs);
|
187 | next();
|
188 | });
|
189 | },
|
190 | function(next) {
|
191 | inquirer.prompt([
|
192 | {
|
193 | type: "input",
|
194 | name: "publicKeys",
|
195 | message: "Enter public keys of dapp forgers - hex array, use ',' for separator",
|
196 | default: account.keypair.publicKey,
|
197 | validate: function (value) {
|
198 | var done = this.async();
|
199 |
|
200 | var publicKeys = value.split(",");
|
201 |
|
202 | if (publicKeys.length == 0) {
|
203 | done("DApp requires at least 1 public key");
|
204 | return;
|
205 | }
|
206 |
|
207 | for (var i in publicKeys) {
|
208 | try {
|
209 | var b = new Buffer(publicKeys[i], "hex");
|
210 | if (b.length != 32) {
|
211 | done("Invalid public key: " + publicKeys[i]);
|
212 | return;
|
213 | }
|
214 | } catch (e) {
|
215 | done("Invalid hex for public key: " + publicKeys[i]);
|
216 | return;
|
217 | }
|
218 | }
|
219 | done(true);
|
220 | }
|
221 | }
|
222 | ], function (result) {
|
223 | if (!result.publicKeys) {
|
224 | return next("invalid dapp forger public keys");
|
225 | }
|
226 | console.log("Creating DApp genesis block");
|
227 | dappBlock = dappHelper.new(account, result.publicKeys.split(","));
|
228 | next();
|
229 | });
|
230 | },
|
231 | function(next) {
|
232 | console.log("Fetching Asch Dapps SDK");
|
233 | fs.exists(dappsPath, function (exists) {
|
234 | if (!exists) {
|
235 | fs.mkdir(dappsPath, next);
|
236 | } else {
|
237 | next();
|
238 | }
|
239 | });
|
240 | },
|
241 | function(next) {
|
242 | dappPath = path.join(dappsPath, dappTrs.id);
|
243 | fsExtra.copy(templatePath, dappPath, {clobber: true}, next);
|
244 | },
|
245 | function(next) {
|
246 | console.log("Saving genesis block");
|
247 | var genesisBlockJson = JSON.stringify(dappBlock, null, 2);
|
248 | fs.writeFile(path.join(dappPath, "genesis.json"), genesisBlockJson, "utf8", next);
|
249 | },
|
250 | function(next) {
|
251 | console.log("Saving dapp meta information");
|
252 | var dappParamsJson = JSON.stringify(dappParams, null, 2);
|
253 | fs.writeFile(path.join(dappPath, "dapp.json"), dappParamsJson, "utf8", next);
|
254 | },
|
255 | function (next) {
|
256 | console.log("Registering dapp in localnet");
|
257 | var api = new Api({port: 4096});
|
258 | api.broadcastTransaction(dappTrs, function (err) {
|
259 | if (err) {
|
260 | next("Failed to register dapp: " + err);
|
261 | } else {
|
262 | next();
|
263 | }
|
264 | });
|
265 | }
|
266 | ], function(err) {
|
267 | if (err) {
|
268 | console.log(err);
|
269 | } else {
|
270 | setTimeout(function () {
|
271 | console.log("Done (DApp id is " + dappTrs.id + ")");
|
272 | }, 10000);
|
273 | }
|
274 | });
|
275 | }
|
276 |
|
277 | function changeDapp() {
|
278 | inquirer.prompt([
|
279 | {
|
280 | type: "confirm",
|
281 | name: "confirmed",
|
282 | message: "Existing blockchain will be replaced, are you sure?",
|
283 | default: false
|
284 | }
|
285 | ], function (result) {
|
286 | if (result.confirmed) {
|
287 | inquirer.prompt([
|
288 | {
|
289 | type: "password",
|
290 | name: "secret",
|
291 | message: "Enter secret of your testnet account",
|
292 | validate: function (value) {
|
293 | var done = this.async();
|
294 |
|
295 | if (value.length == 0) {
|
296 | done("Secret is too short, minimum is 1 character");
|
297 | return;
|
298 | }
|
299 |
|
300 | if (value.length > 100) {
|
301 | done("Secret is too long, maximum is 100 characters");
|
302 | return;
|
303 | }
|
304 |
|
305 | done(true);
|
306 | }
|
307 | },
|
308 | {
|
309 | type: "input",
|
310 | name: "dappId",
|
311 | message: "Enter DApp id (folder name of dapp)",
|
312 | required: true
|
313 | },
|
314 | ], function (result) {
|
315 | var account = accountHelper.account(result.secret);
|
316 | var dappId = result.dappId;
|
317 | var publicKeys = [];
|
318 |
|
319 | var dappPath = path.join(".", "dapps", dappId);
|
320 | var dappGenesis = JSON.parse(fs.readFileSync(path.join(dappPath, "genesis.json"), "utf8"));
|
321 |
|
322 | inquirer.prompt([
|
323 | {
|
324 | type: "confirm",
|
325 | name: "confirmed",
|
326 | message: "Continue with exists forgers public keys",
|
327 | required: true,
|
328 | }], function (result) {
|
329 | if (result.confirmed) {
|
330 | publicKeys = dappGenesis.delegates;
|
331 | }
|
332 |
|
333 | inquirer.prompt([
|
334 | {
|
335 | type: "input",
|
336 | name: "publicKeys",
|
337 | message: "Enter public keys of dapp forgers - hex array, use ',' for separator",
|
338 | default: account.keypair.publicKey,
|
339 | validate: function (value) {
|
340 | var done = this.async();
|
341 |
|
342 | var publicKeys = value.split(",");
|
343 |
|
344 | if (publicKeys.length == 0) {
|
345 | done("DApp requires at least 1 public key");
|
346 | return;
|
347 | }
|
348 |
|
349 | for (var i in publicKeys) {
|
350 | try {
|
351 | var b = new Buffer(publicKeys[i], "hex");
|
352 | if (b.length != 32) {
|
353 | done("Invalid public key: " + publicKeys[i]);
|
354 | return;
|
355 | }
|
356 | } catch (e) {
|
357 | done("Invalid hex for public key: " + publicKeys[i]);
|
358 | return;
|
359 | }
|
360 | }
|
361 |
|
362 | done(true);
|
363 | }
|
364 | }
|
365 | ], function (result) {
|
366 | console.log("Creating DApp genesis block");
|
367 |
|
368 | var dappBlock = dappHelper.new(account, dappGenesis, result.publicKeys.split(","));
|
369 | var dappGenesisBlockJson = JSON.stringify(dappBlock, null, 2);
|
370 |
|
371 | try {
|
372 | fs.writeFileSync(path.join(dappPath, "genesis.json"), dappGenesisBlockJson, "utf8");
|
373 | } catch (e) {
|
374 | return console.log(err);
|
375 | }
|
376 |
|
377 | console.log("Done");
|
378 | });
|
379 | });
|
380 | });
|
381 | }
|
382 | });
|
383 | }
|
384 |
|
385 | function depositDapp() {
|
386 | inquirer.prompt([
|
387 | {
|
388 | type: "password",
|
389 | name: "secret",
|
390 | message: "Enter secret",
|
391 | validate: function (value) {
|
392 | return value.length > 0 && value.length < 100;
|
393 | },
|
394 | required: true
|
395 | },
|
396 | {
|
397 | type: "input",
|
398 | name: "amount",
|
399 | message: "Enter amount",
|
400 | validate: function (value) {
|
401 | return !isNaN(parseInt(value));
|
402 | },
|
403 | required: true
|
404 | },
|
405 | {
|
406 | type: "input",
|
407 | name: "dappId",
|
408 | message: "DApp Id",
|
409 | required: true
|
410 | },
|
411 | {
|
412 | type: "input",
|
413 | name: "secondSecret",
|
414 | message: "Enter secondary secret (if defined)",
|
415 | validate: function (value) {
|
416 | return value.length < 100;
|
417 | },
|
418 | required: false
|
419 | }
|
420 | ], function (result) {
|
421 | var body = {
|
422 | secret: result.secret,
|
423 | dappId: result.dappId,
|
424 | amount: parseInt(result.amount)
|
425 | };
|
426 |
|
427 | if (result.secondSecret && result.secondSecret.length > 0) {
|
428 | body.secondSecret = result.secondSecret;
|
429 | }
|
430 |
|
431 | inquirer.prompt([
|
432 | {
|
433 | type: "input",
|
434 | name: "host",
|
435 | message: "Host and port",
|
436 | default: "localhost:4096",
|
437 | required: true
|
438 | }
|
439 | ], function (result) {
|
440 | request({
|
441 | url: "http://" + result.host + "/api/dapps/transaction",
|
442 | method: "put",
|
443 | json: true,
|
444 | body: body
|
445 | }, function (err, resp, body) {
|
446 | console.log(err, body);
|
447 | if (err) {
|
448 | return console.log(err.toString());
|
449 | }
|
450 |
|
451 | if (body.success) {
|
452 | console.log(body.transactionId);
|
453 | return;
|
454 | } else {
|
455 | return console.log(body.error);
|
456 | }
|
457 | });
|
458 | });
|
459 | });
|
460 | }
|
461 |
|
462 | function withdrawalDapp() {
|
463 | inquirer.prompt([
|
464 | {
|
465 | type: "password",
|
466 | name: "secret",
|
467 | message: "Enter secret",
|
468 | validate: function (value) {
|
469 | return value.length > 0 && value.length < 100;
|
470 | },
|
471 | required: true
|
472 | },
|
473 | {
|
474 | type: "input",
|
475 | name: "amount",
|
476 | message: "Amount",
|
477 | validate: function (value) {
|
478 | return !isNaN(parseInt(value));
|
479 | },
|
480 | required: true
|
481 | },
|
482 | {
|
483 | type: "input",
|
484 | name: "dappId",
|
485 | message: "Enter DApp id",
|
486 | validate: function (value) {
|
487 | var isAddress = /^[0-9]+$/g;
|
488 | return isAddress.test(value);
|
489 | },
|
490 | required: true
|
491 | }], function (result) {
|
492 |
|
493 | var body = {
|
494 | secret: result.secret,
|
495 | amount: Number(result.amount)
|
496 | };
|
497 |
|
498 | request({
|
499 | url: "http://localhost:4096/api/dapps/" + result.dappId + "/api/withdrawal",
|
500 | method: "post",
|
501 | json: true,
|
502 | body: body
|
503 | }, function (err, resp, body) {
|
504 | if (err) {
|
505 | return console.log(err.toString());
|
506 | }
|
507 |
|
508 | if (body.success) {
|
509 | console.log(body.transactionId);
|
510 | } else {
|
511 | return console.log(body.error);
|
512 | }
|
513 | });
|
514 | });
|
515 | }
|
516 |
|
517 | module.exports = function (program) {
|
518 | program
|
519 | .command("dapps")
|
520 | .description("manage your dapps")
|
521 | .option("-a, --add", "add new dapp")
|
522 | .option("-c, --change", "change dapp genesis block")
|
523 | .option("-d, --deposit", "deposit funds to dapp")
|
524 | .option("-w, --withdrawal", "withdraw funds from dapp")
|
525 | .action(function (options) {
|
526 | if (options.add) {
|
527 | addDapp();
|
528 | } else if (options.change) {
|
529 | changeDapp();
|
530 | } else if (options.deposit) {
|
531 | depositDapp();
|
532 | } else if (options.withdrawal) {
|
533 | withdrawalDapp();
|
534 | } else {
|
535 | console.log("'node dapps -h' to get help");
|
536 | }
|
537 | });
|
538 | } |
\ | No newline at end of file |