Merge pull request #361 from embark-framework/graphviz-contracts

Graphviz contracts
This commit is contained in:
Iuri Matias 2018-04-13 11:29:23 -04:00 committed by GitHub
commit 82726e07e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 187 additions and 1 deletions

View File

@ -18,6 +18,7 @@ class Cmd {
this.simulator(); this.simulator();
this.test(); this.test();
this.reset(); this.reset();
this.graph();
this.upload(); this.upload();
this.versionCmd(); this.versionCmd();
this.otherCommands(); this.otherCommands();
@ -179,6 +180,18 @@ class Cmd {
}); });
} }
graph() {
program
.command('graph [environment]')
.description('generates documentation based on the smart contracts configured')
.action(function (env, options) {
embark.graph({
env: env || 'development',
logfile: options.logfile
});
});
}
reset() { reset() {
program program
.command('reset') .command('reset')
@ -215,6 +228,7 @@ class Cmd {
}); });
} }
} }
module.exports = Cmd; module.exports = Cmd;

110
lib/cmds/graph.js Normal file
View File

@ -0,0 +1,110 @@
const Viz = require('viz.js');
const fs = require('fs');
class GraphGenerator {
constructor(engine) {
this.engine = engine;
}
generate() {
let id = 0;
let contractString = "";
let relationshipString = "";
let idMapping = {};
let contractInheritance = {};
for (let contract in this.engine.contractsManager.contracts) {
id++;
idMapping[contract] = id;
let contractLabel = "";
contractLabel += `${contract}`;
let tooltip = contract;
if(this.engine.contractsManager.contracts[contract].instanceOf !== undefined &&
this.engine.contractsManager.contracts[this.engine.contractsManager.contracts[contract].instanceOf] !== undefined){
contractInheritance[contract] = this.engine.contractsManager.contracts[contract].instanceOf;
contractLabel += ": " + this.engine.contractsManager.contracts[contract].instanceOf;
tooltip += " instance of " + this.engine.contractsManager.contracts[contract].instanceOf;
} else {
contractLabel += "|";
for(let i = 0; i < this.engine.contractsManager.contracts[contract].abiDefinition.length; i++){
switch(this.engine.contractsManager.contracts[contract].abiDefinition[i].type){
case 'fallback':
contractLabel += "«fallback»()\\l";
break;
case 'constructor':
contractLabel += "«constructor»(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
case 'event':
contractLabel += "«event»" + this.engine.contractsManager.contracts[contract].abiDefinition[i].name + "(";
this.engine.contractsManager.contracts[contract].abiDefinition[i].inputs.forEach(function(elem, index){
contractLabel += (index == 0 ? "" : ", ") + elem.type;
});
contractLabel += ")\\l";
break;
default: break;
}
}
let fHashes = this.engine.contractsManager.contracts[contract].functionHashes;
if(fHashes != {} && fHashes != undefined){
for(let method in this.engine.contractsManager.contracts[contract].functionHashes){
contractLabel += method + '\\l';
}
}
}
let others = '';
if(!this.engine.contractsManager.contracts[contract].deploy){
others = 'fontcolor="#c3c3c3", color="#a0a0a0"';
tooltip += " (not deployed)";
}
contractString += `${id}[label = "{${contractLabel}}", tooltip="${tooltip}", fillcolor=gray95, ${others}]\n`;
}
for (let c in this.engine.contractsManager.contractDependencies){
let contractDependencies = Array.from(new Set(this.engine.contractsManager.contractDependencies[c]));
contractDependencies.forEach(function(d){
if(idMapping[c] !== undefined && idMapping[d] !== undefined){
relationshipString += `${idMapping[d]}->${idMapping[c]}[constraint=true, arrowtail=diamond, tooltip="${c} uses ${d}"]\n`;
}
});
}
for (let c in contractInheritance){
relationshipString += `${idMapping[contractInheritance[c]]}->${idMapping[c]}[tooltip="${c} instance of ${contractInheritance[c]}"]\n`;
}
let dot = `
digraph Contracts {
node[shape=record,style=filled]
edge[dir=back, arrowtail=empty]
${contractString}
${relationshipString}
}`;
let svg = Viz(dot);
let filename = "diagram.svg";
fs.writeFileSync(filename, svg, (err) => {
if (err) throw err;
});
}
}
module.exports = GraphGenerator;

View File

@ -17,6 +17,7 @@ class DeployManager {
this.gasLimit = false; this.gasLimit = false;
this.fatalErrors = false; this.fatalErrors = false;
this.deployOnlyOnConfig = false; this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
} }
deployContracts(done) { deployContracts(done) {
@ -33,6 +34,13 @@ class DeployManager {
self.contractsManager.deployOnlyOnConfig = self.deployOnlyOnConfig; // temporary, should refactor self.contractsManager.deployOnlyOnConfig = self.deployOnlyOnConfig; // temporary, should refactor
self.contractsManager.build(callback); self.contractsManager.build(callback);
}, },
function checkCompileOnly(contractsManager, callback){
if(self.onlyCompile){
self.events.emit('contractsDeployed', contractsManager);
return done();
}
return callback(contractsManager, null);
},
function checkWeb3IsConnected(contractsManager, callback) { function checkWeb3IsConnected(contractsManager, callback) {
if (!self.web3) { if (!self.web3) {
return callback(Error("no web3 instance found")); return callback(Error("no web3 instance found"));

View File

@ -153,7 +153,8 @@ class Engine {
logger: this.logger, logger: this.logger,
plugins: this.plugins, plugins: this.plugins,
events: this.events, events: this.events,
contractsManager: this.contractsManager contractsManager: this.contractsManager,
onlyCompile: options.onlyCompile
}); });
this.events.on('file-event', function (fileType, _path) { this.events.on('file-event', function (fileType, _path) {

View File

@ -194,6 +194,53 @@ class Embark {
return new Test(options); return new Test(options);
} }
graph(options) {
options.onlyCompile = true;
let engine = new Engine({
env: options.env,
version: this.version,
embarkConfig: options.embarkConfig || 'embark.json',
logfile: options.logfile
});
engine.init();
async.parallel([
function (callback) {
let pluginList = engine.plugins.listPlugins();
if (pluginList.length > 0) {
engine.logger.info("loaded plugins: " + pluginList.join(", "));
}
engine.startMonitor();
engine.startService("libraryManager");
engine.startService("pipeline");
engine.startService("codeGenerator");
engine.startService("deployment", {onlyCompile: true});
engine.deployManager.deployContracts(function (err) {
callback(err);
});
}
], function (err, _result) {
if (err) {
engine.logger.error(err.message);
engine.logger.info(err.stack);
} else {
const GraphGenerator = require('./cmds/graph.js');
let graphGen = new GraphGenerator(engine);
graphGen.generate();
engine.logger.info("Done".underline);
process.exit();
}
});
}
reset() { reset() {
let resetCmd = require('./cmds/reset.js'); let resetCmd = require('./cmds/reset.js');
resetCmd(); resetCmd();

5
package-lock.json generated
View File

@ -10063,6 +10063,11 @@
"extsprintf": "1.3.0" "extsprintf": "1.3.0"
} }
}, },
"viz.js": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/viz.js/-/viz.js-1.8.1.tgz",
"integrity": "sha512-KrSNgnIxec+JCAqDPliO6xYA69ToH2WTYB2Kbt8Bp/XRUvm23rTyfffFi4rvQLFkIRNUz/xCnnqhh/gChhsgGA=="
},
"vlq": { "vlq": {
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",

View File

@ -58,6 +58,7 @@
"underscore": "^1.8.3", "underscore": "^1.8.3",
"underscore.string": "^3.3.4", "underscore.string": "^3.3.4",
"url-loader": "^0.6.2", "url-loader": "^0.6.2",
"viz.js": "^1.8.1",
"web3": "1.0.0-beta.27", "web3": "1.0.0-beta.27",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"window-size": "^1.1.0" "window-size": "^1.1.0"