diff --git a/v3.0/caterpillar-core/.gitignore b/v3.0/caterpillar-core/.gitignore new file mode 100644 index 0000000..a7ed6e6 --- /dev/null +++ b/v3.0/caterpillar-core/.gitignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + +# Autogenerated folder by gulp +out + +# Generated by vscode +.vscode + + +# Debug log from npm +npm-debug.log + +.node-xmlhttprequest-sync* \ No newline at end of file diff --git a/v3.0/caterpillar-core/README.md b/v3.0/caterpillar-core/README.md new file mode 100644 index 0000000..bd5b56f --- /dev/null +++ b/v3.0/caterpillar-core/README.md @@ -0,0 +1,14 @@ +# README +## This the readme for your application "node-express-typescript" +------------------- +### Visual Studio Code has *awesome* Markdown support! + +* Split the editor (`Cmd+\` on OSX or `Ctrl+\` on Windows and Linux) +* Toggle preview (`Shift+CMD+V` on OSX or `Shift+Ctrl+V` on Windows and Linux) +* Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (OSX) to see a list of Markdown snippets + +### For more information +* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown) +* [Markdown Syntax Reference](http://daringfireball.net) + +**Enjoy!** \ No newline at end of file diff --git a/v3.0/caterpillar-core/gulpfile.js b/v3.0/caterpillar-core/gulpfile.js new file mode 100644 index 0000000..03b26e4 --- /dev/null +++ b/v3.0/caterpillar-core/gulpfile.js @@ -0,0 +1,76 @@ +var gulp = require('gulp'); +var less = require('gulp-less'); +var path = require('path'); +var mocha = require('gulp-mocha'); +var browserSync = require('browser-sync'); +var nodemon = require('gulp-nodemon'); +var cp = require('child_process'); +var tsb = require('gulp-tsb'); + + +// compile less files from the ./styles folder +// into css files to the ./public/stylesheets folder +gulp.task('less', function() { + return gulp.src('./src/styles/**/*.less') + .pipe(less({ + paths: [path.join(__dirname, 'less', 'includes')] + })) + .pipe(gulp.dest('./out/public/stylesheets')); +}); + + +// run mocha tests in the ./tests folder +gulp.task('test', function() { + return gulp.src('./tests/out/test*.js', { read: false }) + // gulp-mocha needs filepaths so you can't have any plugins before it + .pipe(mocha()); +}); + +// run browser-sync on for client changes +gulp.task('browser-sync', ['nodemon', 'watch'], function() { + browserSync.init(null, { + open: false, + proxy: "http://localhost:3000", + files: ["out/**/*.*", "out/routes/**/*.*", "out/public/**/*.*", "out/views/**/*.*"], + port: 7000, + }); +}); + +// run nodemon on server file changes +gulp.task('nodemon', function(cb) { + var started = false; + + return nodemon({ + script: 'out/www.js', + watch: ['out/**/*.js'] + }).on('start', function() { + if (!started) { + cb(); + started = true; + } + }).on('restart', function onRestart() { + setTimeout(function reload() { + browserSync.reload({ + stream: false + }); + }, 500); // browserSync reload delay + }); +}); + +// TypeScript build for /src folder +var tsConfigSrc = tsb.create('src/tsconfig.json'); +gulp.task('build', function() { + return gulp.src('./src/**/*.ts') + .pipe(tsConfigSrc()) + .pipe(gulp.dest('./out')); +}); + +// watch for any TypeScript or LESS file changes +// if a file change is detected, run the TypeScript or LESS compile gulp tasks +gulp.task('watch', function() { + gulp.watch('src/**/*.ts', ['build']); + // gulp.watch('src/styles/**/*.less', ['less']); +}); + +gulp.task('buildAll', ['build', 'less']); +gulp.task('default', ['browser-sync']); \ No newline at end of file diff --git a/v3.0/caterpillar-core/package.json b/v3.0/caterpillar-core/package.json new file mode 100644 index 0000000..77124a9 --- /dev/null +++ b/v3.0/caterpillar-core/package.json @@ -0,0 +1,61 @@ +{ + "name": "node-express-typescript", + "description": "node-express-typescript Sample Application (TypeScript)", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./out/www.js", + "test": "mocha ./tests/out/test.js", + "compile": "node ./node_modules/typescript/bin/tsc -p ./src", + "compiletests": "node ./node_modules/typescript/bin/tsc -p ./tests", + "antlr4ts": "antlr4ts -o ./src/models/dynamic_binding -visitor ./src/models/dynamic_binding/binding_grammar.g4" + }, + "dependencies": { + "antlr4": "^4.7.1", + "antlr4ts": "^0.4.1-alpha.0", + "bignumber.js": "^4.0.1", + "body-parser": "~1.13.2", + "bpmn-moddle": "^0.14.0", + "cookie-parser": "~1.3.5", + "cors": "^2.8.3", + "debug": "~2.2.0", + "ejs": "^2.5.6", + "express": "~4.13.1", + "jade": "~1.11.0", + "mongoose": "^4.13.7", + "morgan": "~1.6.1", + "serve-favicon": "~2.3.0", + "solc": "^0.4.25", + "web3": "^0.19.0", + "web3-eth-abi": "^1.0.0-beta.52" + }, + "devDependencies": { + "@types/antlr4": "^4.7.0", + "@types/body-parser": "^1.16.1", + "@types/cookie-parser": "^1.3.30", + "@types/debug": "^0.0.29", + "@types/express": "^4.0.35", + "@types/gulp": "^4.0.2", + "@types/gulp-less": "^0.0.30", + "@types/gulp-mocha": "^0.0.30", + "@types/gulp-nodemon": "^0.0.30", + "@types/marked": "^0.0.28", + "@types/mocha": "^2.2.40", + "@types/morgan": "^1.7.32", + "@types/node": "^7.0.10", + "@types/power-assert": "^1.4.29", + "@types/serve-favicon": "^2.2.28", + "antlr4ts-cli": "^0.4.0-alpha.4", + "browser-sync": "^2.18.8", + "gulp": "^3.9.1", + "gulp-less": "^3.3.0", + "gulp-mocha": "^4.1.0", + "gulp-nodemon": "^2.2.1", + "gulp-tsb": "^2.0.3", + "marked": "^0.3.6", + "mocha": "^3.2.0", + "nodemon": "^1.11.0", + "typescript": "^2.6.2", + "xml2js": "^0.4.19" + } +} diff --git a/v3.0/caterpillar-core/src/app.ts b/v3.0/caterpillar-core/src/app.ts new file mode 100644 index 0000000..b045096 --- /dev/null +++ b/v3.0/caterpillar-core/src/app.ts @@ -0,0 +1,64 @@ +import * as express from 'express'; +import * as logger from 'morgan'; +import * as bodyParser from 'body-parser'; +import * as cors from 'cors'; +import * as mongoose from 'mongoose'; + +import * as path from 'path'; +import models from './models/models.controller'; + +const app: express.Express = express(); + +// uncomment after placing your favicon in /public +//app.use(favicon(__dirname + '/public/favicon.ico')); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(cors()); +app.use(bodyParser.urlencoded({ extended: false })); + +app.use('/', models); + +// catch 404 and forward to error handler +app.use((req, res, next) => { + var err = new Error('Not Found'); + err['status'] = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + + app.use((error: any, req, res, next) => { + console.log(error); + res.status(error['status'] || 500); + res.render('error', { + message: error.message, + error + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use((error: any, req, res, next) => { + res.status(error['status'] || 500); + res.render('error', { + message: error.message, + error: {} + }); + return null; +}); + +mongoose.Promise = global.Promise; +mongoose.connect('mongodb://localhost:27017/caterpillarRepo', function(error){ + if(error){ + throw error; + }else{ + console.log('Conectado a MongoDB'); + } +}); + +export default app; diff --git a/v3.0/caterpillar-core/src/models/abstract/AbstractFactory.sol b/v3.0/caterpillar-core/src/models/abstract/AbstractFactory.sol new file mode 100644 index 0000000..a3f4812 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/AbstractFactory.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.25; + +contract AbstractFactory { + address internal worklist = address(0); + + function setWorklist(address _worklist) public { + worklist = _worklist; + } + + function newInstance(address parent, address globalFactory) public returns(address); + function startInstanceExecution(address processAddress) public; +} diff --git a/v3.0/caterpillar-core/src/models/abstract/AbstractProcess.sol b/v3.0/caterpillar-core/src/models/abstract/AbstractProcess.sol new file mode 100644 index 0000000..3ac39f7 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/AbstractProcess.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.4.25; + +import "AbstractRegistry"; + + +contract AbstractProcess { + address internal owner; + address internal parent; + address internal worklist; + uint internal instanceIndex; + address internal processRegistry; + + constructor(address _parent, address _worklist, address _processRegistry) public { + owner = msg.sender; + parent = _parent; + worklist = _worklist; + processRegistry = _processRegistry; + } + + function setInstanceIndex(uint _instanceIndex) public { + require(msg.sender == parent); + instanceIndex = _instanceIndex; + } + + function findParent() public view returns(address) { + return parent; + } + + function handleEvent(bytes32 code, bytes32 eventType, uint _instanceIndex, bool isInstanceCompleted) public; + function killProcess() public; + function startExecution() public; + function broadcastSignal() public; + + function killProcess(uint processElementIndex, uint marking, uint startedActivities) internal returns(uint, uint); + function broadcastSignal(uint tmpMarking, uint tmpStartedActivities, uint sourceChild) internal returns(uint, uint); + + function propagateEvent(bytes32 code, bytes32 eventType, uint tmpMarking, uint tmpStartedActivities, uint sourceChild) internal returns(uint, uint) { + if (eventType == "Error" || eventType == "Terminate") + (tmpMarking, tmpStartedActivities) = killProcess(0, tmpMarking, tmpStartedActivities); + else if (eventType == "Signal") + (tmpMarking, tmpStartedActivities) = broadcastSignal(tmpMarking, tmpStartedActivities, sourceChild); + if (parent != 0) + AbstractProcess(parent).handleEvent(code, eventType, instanceIndex, tmpMarking | tmpStartedActivities == 0); + return (tmpMarking, tmpStartedActivities); + } +} diff --git a/v3.0/caterpillar-core/src/models/abstract/AbstractRegistry.sol b/v3.0/caterpillar-core/src/models/abstract/AbstractRegistry.sol new file mode 100644 index 0000000..c4c53d6 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/AbstractRegistry.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.4.25; + +contract AbstractRegistry { + + function registerFactory(bytes32 bundleId, address factory) public; + + function registerWorklist(bytes32 bundleId, address worklist) public; + + function allInstances() public returns(address[]); + + function newInstanceFor(uint nodeIndex, address parent) public returns(address); + + function newBundleInstanceFor(bytes32 bundleId, address parent) public returns(address); + + function bundleFor(address processInstance) public returns(bytes32); + + function worklistBundleFor(address worklist) public returns(bytes32); + + // Functions for Dynamic Bindings + function bindingPolicyFor(address processInstance) public view returns(bytes32); + + function taskRoleMapFor(address processInstance) public view returns(bytes32); + + function relateProcessToPolicy(bytes32 bundleId, bytes32 _taskRole, bytes32 _policy) external; + + function canPerform(address actor, address processCase, uint taskIndex) external view returns(bool); +} diff --git a/v3.0/caterpillar-core/src/models/abstract/AbstractWorklist.sol b/v3.0/caterpillar-core/src/models/abstract/AbstractWorklist.sol new file mode 100644 index 0000000..dd30f5c --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/AbstractWorklist.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.4.25; + +contract IRFunct { + function findRuntimePolicy(address pCase) public view returns(address); + function canPerform(address actor, address pCase, uint taskIndex) public view returns(bool); +} + +contract AbstractWorklist { + + struct Workitem { + uint elementIndex; + address processInstanceAddr; + } + + Workitem[] internal workitems; + address internal runtimeRegistry; + + function updateRuntimeRegistry(address _runtimeRegistry) public { + runtimeRegistry = _runtimeRegistry; + } + + function workItemsFor(uint elementIndex, address processInstance) external view returns(uint) { + uint reqIndex = 0; + for (uint i = 0; i < workitems.length; i++) { + if (workitems[i].elementIndex == elementIndex && workitems[i].processInstanceAddr == processInstance) + reqIndex |= uint(1) << i; + } + return reqIndex; + } + + function processInstanceFor(uint workitemId) public view returns(address) { + require(workitemId < workitems.length); + return workitems[workitemId].processInstanceAddr; + } + + function elementIndexFor(uint workitemId) public view returns(uint) { + require(workitemId < workitems.length); + return workitems[workitemId].elementIndex; + } + + function canPerform(address actor, address pCase, uint elementIndex) internal view returns(bool) { + return IRFunct(IRFunct(runtimeRegistry).findRuntimePolicy(pCase)).canPerform(actor, pCase, elementIndex); + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/abstract/BidingPolicy.sol b/v3.0/caterpillar-core/src/models/abstract/BidingPolicy.sol new file mode 100644 index 0000000..208f2d4 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/BidingPolicy.sol @@ -0,0 +1,235 @@ +pragma solidity ^0.4.25; + +/// Index 0 is reserved to identify in the mappings (see Binding Access Control) the elements that aren't binded yet. +/// This is a restriction of Solidity, by definition all the keys are in the mapping, thus the zero (null) value would represent non existence. +/// Index 1 is reserved to the case-creator which can be seen as a default role in our schema. + + +/// EXAMPLE +// { +// Customer is case-creator, +// Customer nominates Supplier endorsed-by Customer, +// Supplier nominates CarrierCandidate not in CarrierCandidate endorsed-by CarrierCandidate, +// Supplier nominates Carrier in CarrierCandidate endorsed-by Carrier and Customer +// } +// { +// CarrierCandidate dismisses CarrierCandidate not in Carrier +// } + + +/// MAPPING OF ROLES TO INDEXES (INTEGER) FOR THE RUNNING EXAMPLE ///////// +/// Actor-Identifier -> Index (Index-BitSet) +// self -> 1 (2) +// Customer -> 2 (4) case-creator +// Supplier -> 3 (8) +// CarrierCandidate -> 4 (16) +// Carrier -> 5 (32) + + +library BidingPolicy { + + enum EndorsmentState {ENDORSED, PENDING, REJECTED, UNBINDING} + + //////////////////////////////////////////////////////////////////////////// + /// NOMINATION (BINDING) OPERATIONS /// + //////////////////////////////////////////////////////////////////////////// + + /** @dev This function retrieves the allowed case creators. + * @return bitset encoding all the indexes of roles allowed as case creators. + */ + function getCaseCreator() public pure returns(uint) { + return 2; + } + + /** @dev This function evaluates if a role is allowed to nominate another role. + * @param nominatorRoles bitset encoding the indexes of the roles of the actor that is trying to nominate, + * @param nomineeRole index of the role to nominate, + * @return true if @nominatorRole can nominate @nomineeRole according to the binding policy, otherwise false. + */ + function checkNominatorCandidates(uint nominatorRoles, uint8 nomineeRole) public pure returns(bool) { + + uint nomineeRoleMask = uint(1) << nomineeRole; + + // @nominatorsMask will be a bitset encoding the indexes of all the roles allowed to nominate @nomineeRole + uint nominatorsMask = 0; + + if (nomineeRole == 3) // Supplier + nominatorsMask = 4; // nominated by Customer + else if (nomineeRoleMask & 48 != 0) // CarrierCandidate and Carrier + nominatorsMask = 8; // nominated by Supplier + + // The nominator holds at least one of the roles defined as nominators in the policy. + return nominatorsMask & nominatorRoles != 0; + } + + /** @dev This function evaluates if for a given role to nominate the conditions defined are fulfilled + * (keywords IN, NOT IN followed by in the language). + * The conditions must be provided as a disjunction of conjunctions, e.g., (A and B) or (D and E) ..., + * such that they are represented as an array where every conjunction is encoded as a bitset. + * @param nomineeRole index of the role to nominate, + * @param nomineeCurrentRoles bitset encoding the roles previously granted to the actor to be nominated as @nomineeRole, + * @param nominatorRoles bitset encoding the roles previously granted to the nominator, + * @return true if the conditions are fulfilled, false otherwise. + */ + function checkNominationRestrictions(uint8 nomineeRole, uint nomineeCurrentRoles, uint nominatorRoles) public pure returns(bool) { + + // Checking the union is required in cases like: A nominates B, C nominates B + uint nominatorNomineeMask = nominatorRoles | (uint(1) << nomineeRole); + + if (nominatorNomineeMask & 24 == 24) // Supplier nominates CarrierCandidate (8 | 16) + return !(nomineeCurrentRoles & 16 == 16); // CarrierCandidate not in CarrierCandidate + else if (nominatorNomineeMask & 40 == 40) // Supplier nominates Carrier (8 | 32) + return nomineeCurrentRoles & 16 == 16; // Carrier in CarrierCandidate + + return true; + + // In case of conditions of the form (A and B) or (C and D) or ... the evaluation is made as: + // return nomineeCurrentRoles & (A | B) == (A | B) || nomineeCurrentRoles & (C | D) == (C | D) ...; + } + + /** @dev This function evaluates if for a given role must be endorsed when nominated, + * @param nomineeRole index of the role to nominate, + * @param nominatorRoles bitset encoding the roles previously granted to the nominator, + * @return true if @nomineeRole requires endorsment, false otherwise + */ + function requireNominationEndrosment(uint8 nomineeRole, uint nominatorRoles) public pure returns(bool) { + uint nominatorNomineeMask = nominatorRoles | (uint(1) << nomineeRole); + return nominatorNomineeMask == 12 + || nominatorNomineeMask == 24 + || nominatorNomineeMask == 40; + } + + /** @dev This function evaluates if for a given role to endorse the conditions defined are fulfilled + * (keywords ENDORSED BY followed by a in the language). + * The conditions must be provided as a disjunction of conjunctions, e.g., (A and B) or (D and E) ..., + * such that they are represented as an array where every conjunction is encoded as a bitset. + * @param nomineeRole index of the role that was nominated, + * @param endorserRoles bitset encoding the roles granted to the actor who is endorsing, + * @param endorsedBy bitset encoding the roles that already endorsed the nomination of @nomineeRole, + * @param rejectedBy bitset encoding the roles that already rejected the nomination of @nomineeRole, + * @param isAccepted true if the the endorser is accepting the nomination/dismisses, false otherwise + * @return true if the conditions are fulfilled, false otherwise. + */ + function checkNominationEndorsment(uint8 nomineeRole, uint nominatorRoles, uint endorserRoles, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(EndorsmentState) { + + // An endorser is allowed to endorse/reject a nomination once + require((endorsedBy | rejectedBy) & endorserRoles == 0); + + // An role may be nominated by several roles, under diferent conditions/endorsement requirements + uint nominatorNomineeMask = nominatorRoles | (uint(1) << nomineeRole); + + // Considerig the endorsment expression as a disjunction of conjunctions + // 1- ENDORSED -> all the roles in at least a conjunction set accepted the endorsement, + // 2- REJECTED -> every conjunction set contains at least one role who rejected the endorsement, + // 3- PENDING -> none of the conditions 1 and 2 are fulfilled yet, + // i.e. exist at least a conjunction set without rejections and with roles pending to endorse the nomination. + + // ENDORSED IF: + // Exist a conjunction set where: + // [(endorsedBy | endorsedRoles) & conjuntion_set == conjunction_set] is true + + // REJECTED IF: + // for all conjunction set: + // [(rejectedBy | endorserRoles) & conjunction_set != 0] is true + + + // WARNING: An actor may hold more than one role, thus "endorserRoles" may include more than one of the endorser roles if they + // were granted to the actor before. We cannot predict which role is the actor using in the endorsement at least he explicitly + // provides that as parameter. Note that at runtime, we only know an actor (address) is trying to endorse, no the role such actor + // wants to play in such endorsement, at least we manage something like a sesion and provide as input parameter the role of the + // actor logged in session. + + endorsedBy |= endorserRoles; + rejectedBy |= endorserRoles; + + if(nominatorNomineeMask & 12 == 12) { // Customer nominates Supplier (4 | 8) + require(endorserRoles & 8 != 0); // Validating the user trying to endorse is allowed as endorser. + if(isAccepted && endorsedBy & 8 == 8) // endorsed by self[Supplier] (8) + return EndorsmentState.ENDORSED; + else if (!isAccepted && rejectedBy & 8 != 0) + return EndorsmentState.REJECTED; + } + else if (nominatorNomineeMask & 24 == 24) { // Supplier nominates CarrierCandidate (8 | 16) + require(endorserRoles & 16 != 0); + if(isAccepted && endorsedBy & 16 == 16) // endorsed by self[CarrierCandidate] (16) + return EndorsmentState.ENDORSED; + else if (!isAccepted && rejectedBy & 16 != 0) + return EndorsmentState.REJECTED; + } + else if(nominatorNomineeMask & 40 == 40) { // Supplier nominates Carrier (8 | 32) + require(endorserRoles & 36 != 0); + if(isAccepted && endorsedBy & 36 == 36) // endorsed by self[Carrier] and Customer (32 | 4) + return EndorsmentState.ENDORSED; + else if (!isAccepted && rejectedBy & 36 != 0) + return EndorsmentState.REJECTED; + } + return EndorsmentState.PENDING; + } + + //////////////////////////////////////////////////////////////////////////// + /// DISMISSAL (UNBINDING) OPERATIONS /// + //////////////////////////////////////////////////////////////////////////// + + + /** @dev This function evaluates if a role is allowed to dismiss another role. + * @param dismissalRoles bitset encoding the indexes of the role that is trying to dismiss, + * @param nomineeRole index of the role to dismiss, + * @return true if @dismissalRoles can dismiss @nomineeRole according to the binding policy, otherwise false. + */ + function checkDismissesCandidates(uint dismissalRoles, uint8 nomineeRole) public pure returns(bool) { + + uint nomineeRoleMask = uint(1) << nomineeRole; + uint dismissalsMask = 0; + + if (nomineeRoleMask & 4 == 4) // CarrierCandidate + dismissalsMask = 16; // dismissed by CarrierCandidate + + return dismissalsMask & dismissalRoles != 0; + } + + + /** @dev This function evaluates if for a given role to dismiss the conditions defined are fulfilled + * (keywords IN, NOT IN followed by in the language). + * The conditions must be provided as a disjunction of conjunctions, e.g., (A and B) or (D and E) ..., + * such that they are represented as an array where every conjunction is encoded as a bitset. + * @param nomineeRole index of the role to dismiss, + * @param nomineeCurrentRoles bitset encoding the roles previously granted to the actor to be dismissed as @nomineeRole, + * @return true if the conditions are fulfilled, false otherwise. + */ + function checkDismissalRestrictions(uint8 nomineeRole, uint nomineeCurrentRoles, uint dismissalRoles) public pure returns(bool) { + + // Checking the union is required in cases like: A dismisses B, C dismisses B + uint dismissalNomineeMask = dismissalRoles | (uint(1) << nomineeRole); + + if (dismissalNomineeMask & 16 == 16) // CarrierCandidate dismisses CarrierCandidate + return !(nomineeCurrentRoles & 32 == 32); // CarrierCandidate not in Carrier + + return true; + + } + + /** @dev This function evaluates if for a given role must be endorsed when dismissed, + * @param nomineeRole index of the role to dismiss, + * @param dismisalRoles bitset encoding the roles previously granted to the dismissal, + * @return true if @nomineeRole requires endorsment, false otherwise + */ + function requireDismissalEndrosment(uint8 nomineeRole, uint dismisalRoles) public pure returns(bool) { + return true; + } + + /** @dev This function evaluates if for a given role to endorse the conditions defined are fulfilled + * (keywords ENDORSED BY followed by a in the language). + * The conditions must be provided as a disjunction of conjunctions, e.g., (A and B) or (D and E) ..., + * such that they are represented as an array where every conjunction is encoded as a bitset. + * @param nomineeRole index of the role that was nominated, + * @param endorserRoles bitset encoding the roles granted to the actor who is endorsing, + * @param endorsedBy bitset encoding the roles that already endorsed the nomination of @nomineeRole, + * @param rejectedBy bitset encoding the roles that already rejected the nomination of @nomineeRole, + * @param isAccepted true if the the endorser is accepting the nomination/dismisses, false otherwise + * @return true if the conditions are fulfilled, false otherwise. + */ + function checkDismissalEndorsment(uint8 nomineeRole, uint dismissalRoles, uint endorserRoles, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(EndorsmentState) { + return EndorsmentState.ENDORSED; + } + +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/abstract/ProcessRegistry.sol b/v3.0/caterpillar-core/src/models/abstract/ProcessRegistry.sol new file mode 100644 index 0000000..178a26b --- /dev/null +++ b/v3.0/caterpillar-core/src/models/abstract/ProcessRegistry.sol @@ -0,0 +1,117 @@ +pragma solidity ^0.4.25; + +contract IFunct { + // WorkList functions + function updateRuntimeRegistry(address _runtimeRegistry) public; + // Factory Functions + function setWorklist(address _worklist) public; + function startInstanceExecution(address processAddress) public; + function newInstance(address parent, address globalFactory) public returns(address); + function findParent() public view returns(address); +} + +contract ProcessRegistry { + + mapping (bytes32 => mapping (uint => bytes32)) private parent2ChildrenBundleId; + mapping (bytes32 => address) private factories; + mapping (bytes32 => bytes32) private policy; + mapping (bytes32 => bytes32) private taskRole; + + mapping (address => bytes32) private instance2Bundle; + mapping (address => address) private instance2PolicyOp; + address[] private instances; + + mapping (address => bytes32) private worklist2Bundle; + + event NewInstanceCreatedFor(address parent, address processAddress); + + function registerFactory(bytes32 bundleId, address factory) external { + factories[bundleId] = factory; + } + + function registerWorklist(bytes32 bundleId, address worklist) external { + address factory = factories[bundleId]; + require(factory != address(0)); + worklist2Bundle[worklist] = bundleId; + IFunct(factory).setWorklist(worklist); + IFunct(worklist).updateRuntimeRegistry(this); + } + + function findRuntimePolicy(address pCase) public view returns(address) { + return instance2PolicyOp[pCase]; + } + + function relateProcessToPolicy(bytes32 bundleId, bytes32 _policy, bytes32 _taskRole) external { + taskRole[bundleId] = _taskRole; + policy[bundleId] = _policy; + } + + + function addChildBundleId(bytes32 parentBundleId, bytes32 processBundleId, uint nodeIndex) external { + parent2ChildrenBundleId[parentBundleId][nodeIndex] = processBundleId; + } + + function newInstanceFor(uint nodeIndex, address parent) public returns(address) { + return newBundleInstanceFor(parent2ChildrenBundleId[instance2Bundle[parent]][nodeIndex], parent, instance2PolicyOp[parent]); + } + + function newBundleInstanceFor(bytes32 bundleId, address parent, address policyOpAddr) public returns(address) { + address factory = factories[bundleId]; + require(factory != address(0)); + address processAddress = IFunct(factory).newInstance(parent, this); + instance2Bundle[processAddress] = bundleId; + instance2PolicyOp[processAddress] = policyOpAddr; + instances.push(processAddress); + IFunct(factory).startInstanceExecution(processAddress); + emit NewInstanceCreatedFor(parent, processAddress); + return processAddress; + } + + function allInstances() external view returns(address[]) { + return instances; + } + + function bindingPolicyFor(address procInstance) external view returns(bytes32) { + bytes32 pId = instance2Bundle[procInstance]; + address pAddr = procInstance; + while(policy[pId].length != 0) { + pAddr = IFunct(pAddr).findParent(); + if(pAddr == 0) + break; + pId = instance2Bundle[pAddr]; + } + return policy[pId]; + } + + function taskRoleMapFor(address procInstance) external view returns(bytes32) { + bytes32 pId = instance2Bundle[procInstance]; + address pAddr = procInstance; + while(taskRole[pId].length != 0) { + pAddr = IFunct(pAddr).findParent(); + if(pAddr == 0) + break; + pId = instance2Bundle[pAddr]; + } + return taskRole[pId]; + } + + function bindingPolicyFromId(bytes32 procId) external view returns(bytes32) { + return policy[procId]; + } + + function taskRoleMapFromId(bytes32 procId) external view returns(bytes32) { + return taskRole[procId]; + } + + function bundleFor(address processInstance) external view returns(bytes32) { + return instance2Bundle[processInstance]; + } + + function childrenFor(bytes32 parent, uint nodeInd) external view returns(bytes32) { + return parent2ChildrenBundleId[parent][nodeInd]; + } + + function worklistBundleFor(address worklist) external view returns(bytes32) { + return worklist2Bundle[worklist]; + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/definitions.ts b/v3.0/caterpillar-core/src/models/definitions.ts new file mode 100644 index 0000000..272de66 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/definitions.ts @@ -0,0 +1,47 @@ + +export class ModelInfo { + name: string; + id: string; + bpmn: string; + solidity: string; + controlFlowInfoMap: Map; + globalNodeMap: Map; + entryContractName: string; + contracts: Map; +} + +export class ParameterInfo { + constructor(public type: string, public name: string) {} +} + +export class OracleInfo { + address: string = null; + functionName: string = null; + functionParameters: Array = new Array(); + + constructor (public oracleName: string) {} +} + +export class ControlFlowInfo { + parent: ControlFlowInfo = null; + isEmbedded: boolean = false; + nodeNameMap: Map = new Map(); + nodeIndexMap: Map = new Map(); + edgeIndexMap: Map = new Map(); + multiinstanceActivities: Map = new Map(); + nonInterruptingEvents: Map = new Map(); + callActivities: Map = new Map(); + externalBundles: Map = new Map(); + catchingMessages: Array; + globalParameters: string = ""; + localParameters: Map>> = new Map(); + oracleInfo: Map = new Map(); + oracleTaskMap: Map = new Map(); + taskRoleMap: Map = new Map(); + + constructor(public self:any, public nodeList: Array, + public edgeList: Array, public sources: Array, + public boundaryEvents: Array) {} +} + + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.g4 b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.g4 new file mode 100644 index 0000000..bf0dcbd --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.g4 @@ -0,0 +1,106 @@ +grammar binding_grammar; + +/* + * Parser Rules + */ + +binding_policy + : binding_set unbinding_set + ; + +binding_set + : LBRACES binding_statement (SEMICOLON binding_statement)* RBRACES + ; + +unbinding_set + : LBRACES (unbinding_statement (SEMICOLON unbinding_statement)* | ) RBRACES + ; + +binding_statement + : is_creator + | (scope_restriction)? nominator NOMINATES nominee (binding_constr)? (endorsement_constr)? + ; + +unbinding_statement + : nominator RELEASES nominee (binding_constr)? (endorsement_constr)? + ; + +is_creator + : role IS CASE_CREATOR + ; + +binding_constr + : NOT IN set_expresion + | IN set_expresion + ; + +endorsement_constr + : ENDORSED_BY set_expresion + ; + +set_expresion + : LPAREN set_expresion RPAREN + | role OR set_expresion + | role AND set_expresion + | role + ; + +scope_restriction + : UNDER subprocess_id COMMA; + +nominator + : role ; + +nominee + : role ; + +role + : (role_id | role_path_expresion | SELF) + ; + +role_path_expresion + : (subprocess_id DOT)+ role_id + ; + +subprocess_id + : IDENTIFIER + ; + +role_id + : IDENTIFIER + ; + +task_id + : IDENTIFIER + ; + +/* + * Lexer Rules + */ + +NOMINATES : 'nominates' ; +RELEASES : 'releases' ; +SELF : 'self' ; +ENDORSED_BY : ( 'endorsed-by' | 'endorsers' ) ; +CASE_CREATOR : 'case-creator' ; + +AND : 'and'; +OR : 'or' ; +IS : 'is' ; +IN : 'in' ; +NOT : 'not'; +UNDER : 'Under'; + +COMMA : ',' ; +DOT : '.' ; +SEMICOLON : ';'; + +LPAREN : '(' ; +RPAREN : ')' ; +LBRACES : '{' ; +RBRACES : '}' ; + +IDENTIFIER : [a-zA-Z_0-9]+ ; + +WS : [ \r\t\u000C\n]+ -> skip ; + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.tokens b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.tokens new file mode 100644 index 0000000..ecda692 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammar.tokens @@ -0,0 +1,37 @@ +NOMINATES=1 +RELEASES=2 +SELF=3 +ENDORSED_BY=4 +CASE_CREATOR=5 +AND=6 +OR=7 +IS=8 +IN=9 +NOT=10 +UNDER=11 +COMMA=12 +DOT=13 +SEMICOLON=14 +LPAREN=15 +RPAREN=16 +LBRACES=17 +RBRACES=18 +IDENTIFIER=19 +WS=20 +'nominates'=1 +'releases'=2 +'self'=3 +'case-creator'=5 +'and'=6 +'or'=7 +'is'=8 +'in'=9 +'not'=10 +'Under'=11 +','=12 +'.'=13 +';'=14 +'('=15 +')'=16 +'{'=17 +'}'=18 diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.tokens b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.tokens new file mode 100644 index 0000000..ecda692 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.tokens @@ -0,0 +1,37 @@ +NOMINATES=1 +RELEASES=2 +SELF=3 +ENDORSED_BY=4 +CASE_CREATOR=5 +AND=6 +OR=7 +IS=8 +IN=9 +NOT=10 +UNDER=11 +COMMA=12 +DOT=13 +SEMICOLON=14 +LPAREN=15 +RPAREN=16 +LBRACES=17 +RBRACES=18 +IDENTIFIER=19 +WS=20 +'nominates'=1 +'releases'=2 +'self'=3 +'case-creator'=5 +'and'=6 +'or'=7 +'is'=8 +'in'=9 +'not'=10 +'Under'=11 +','=12 +'.'=13 +';'=14 +'('=15 +')'=16 +'{'=17 +'}'=18 diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.ts new file mode 100644 index 0000000..3220d23 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarLexer.ts @@ -0,0 +1,157 @@ +// Generated from ./src/models/dynamic_binding/antlr/binding_grammar.g4 by ANTLR 4.6-SNAPSHOT + + +import { ATN } from 'antlr4ts/atn/ATN'; +import { ATNDeserializer } from 'antlr4ts/atn/ATNDeserializer'; +import { CharStream } from 'antlr4ts/CharStream'; +import { Lexer } from 'antlr4ts/Lexer'; +import { LexerATNSimulator } from 'antlr4ts/atn/LexerATNSimulator'; +import { NotNull } from 'antlr4ts/Decorators'; +import { Override } from 'antlr4ts/Decorators'; +import { RuleContext } from 'antlr4ts/RuleContext'; +import { Vocabulary } from 'antlr4ts/Vocabulary'; +import { VocabularyImpl } from 'antlr4ts/VocabularyImpl'; + +import * as Utils from 'antlr4ts/misc/Utils'; + + +export class binding_grammarLexer extends Lexer { + public static readonly NOMINATES=1; + public static readonly RELEASES=2; + public static readonly SELF=3; + public static readonly ENDORSED_BY=4; + public static readonly CASE_CREATOR=5; + public static readonly AND=6; + public static readonly OR=7; + public static readonly IS=8; + public static readonly IN=9; + public static readonly NOT=10; + public static readonly UNDER=11; + public static readonly COMMA=12; + public static readonly DOT=13; + public static readonly SEMICOLON=14; + public static readonly LPAREN=15; + public static readonly RPAREN=16; + public static readonly LBRACES=17; + public static readonly RBRACES=18; + public static readonly IDENTIFIER=19; + public static readonly WS=20; + public static readonly modeNames: string[] = [ + "DEFAULT_MODE" + ]; + + public static readonly ruleNames: string[] = [ + "NOMINATES", "RELEASES", "SELF", "ENDORSED_BY", "CASE_CREATOR", "AND", + "OR", "IS", "IN", "NOT", "UNDER", "COMMA", "DOT", "SEMICOLON", "LPAREN", + "RPAREN", "LBRACES", "RBRACES", "IDENTIFIER", "WS" + ]; + + private static readonly _LITERAL_NAMES: (string | undefined)[] = [ + undefined, "'nominates'", "'releases'", "'self'", undefined, "'case-creator'", + "'and'", "'or'", "'is'", "'in'", "'not'", "'Under'", "','", "'.'", "';'", + "'('", "')'", "'{'", "'}'" + ]; + private static readonly _SYMBOLIC_NAMES: (string | undefined)[] = [ + undefined, "NOMINATES", "RELEASES", "SELF", "ENDORSED_BY", "CASE_CREATOR", + "AND", "OR", "IS", "IN", "NOT", "UNDER", "COMMA", "DOT", "SEMICOLON", + "LPAREN", "RPAREN", "LBRACES", "RBRACES", "IDENTIFIER", "WS" + ]; + public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(binding_grammarLexer._LITERAL_NAMES, binding_grammarLexer._SYMBOLIC_NAMES, []); + + @Override + @NotNull + public get vocabulary(): Vocabulary { + return binding_grammarLexer.VOCABULARY; + } + + + constructor(input: CharStream) { + super(input); + this._interp = new LexerATNSimulator(binding_grammarLexer._ATN, this); + } + + @Override + public get grammarFileName(): string { return "binding_grammar.g4"; } + + @Override + public get ruleNames(): string[] { return binding_grammarLexer.ruleNames; } + + @Override + public get serializedATN(): string { return binding_grammarLexer._serializedATN; } + + @Override + public get modeNames(): string[] { return binding_grammarLexer.modeNames; } + + public static readonly _serializedATN: string = + "\x03\uAF6F\u8320\u479D\uB75C\u4880\u1605\u191C\uAB37\x02\x16\x97\b\x01"+ + "\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06"+ + "\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r"+ + "\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t"+ + "\x12\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x03\x02\x03\x02\x03\x02"+ + "\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03"+ + "\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x04"+ + "\x03\x04\x03\x04\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05"+ + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05"+ + "\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x05\x05X\n\x05\x03\x06\x03\x06"+ + "\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06"+ + "\x03\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\x03\b\x03\b\x03\b\x03"+ + "\t\x03\t\x03\t\x03\n\x03\n\x03\n\x03\v\x03\v\x03\v\x03\v\x03\f\x03\f\x03"+ + "\f\x03\f\x03\f\x03\f\x03\r\x03\r\x03\x0E\x03\x0E\x03\x0F\x03\x0F\x03\x10"+ + "\x03\x10\x03\x11\x03\x11\x03\x12\x03\x12\x03\x13\x03\x13\x03\x14\x06\x14"+ + "\x8D\n\x14\r\x14\x0E\x14\x8E\x03\x15\x06\x15\x92\n\x15\r\x15\x0E\x15\x93"+ + "\x03\x15\x03\x15\x02\x02\x02\x16\x03\x02\x03\x05\x02\x04\x07\x02\x05\t"+ + "\x02\x06\v\x02\x07\r\x02\b\x0F\x02\t\x11\x02\n\x13\x02\v\x15\x02\f\x17"+ + "\x02\r\x19\x02\x0E\x1B\x02\x0F\x1D\x02\x10\x1F\x02\x11!\x02\x12#\x02\x13"+ + "%\x02\x14\'\x02\x15)\x02\x16\x03\x02\x04\x06\x022;C\\aac|\x05\x02\v\f"+ + "\x0E\x0F\"\"\x99\x02\x03\x03\x02\x02\x02\x02\x05\x03\x02\x02\x02\x02\x07"+ + "\x03\x02\x02\x02\x02\t\x03\x02\x02\x02\x02\v\x03\x02\x02\x02\x02\r\x03"+ + "\x02\x02\x02\x02\x0F\x03\x02\x02\x02\x02\x11\x03\x02\x02\x02\x02\x13\x03"+ + "\x02\x02\x02\x02\x15\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\x02\x19\x03"+ + "\x02\x02\x02\x02\x1B\x03\x02\x02\x02\x02\x1D\x03\x02\x02\x02\x02\x1F\x03"+ + "\x02\x02\x02\x02!\x03\x02\x02\x02\x02#\x03\x02\x02\x02\x02%\x03\x02\x02"+ + "\x02\x02\'\x03\x02\x02\x02\x02)\x03\x02\x02\x02\x03+\x03\x02\x02\x02\x05"+ + "5\x03\x02\x02\x02\x07>\x03\x02\x02\x02\tW\x03\x02\x02\x02\vY\x03\x02\x02"+ + "\x02\rf\x03\x02\x02\x02\x0Fj\x03\x02\x02\x02\x11m\x03\x02\x02\x02\x13"+ + "p\x03\x02\x02\x02\x15s\x03\x02\x02\x02\x17w\x03\x02\x02\x02\x19}\x03\x02"+ + "\x02\x02\x1B\x7F\x03\x02\x02\x02\x1D\x81\x03\x02\x02\x02\x1F\x83\x03\x02"+ + "\x02\x02!\x85\x03\x02\x02\x02#\x87\x03\x02\x02\x02%\x89\x03\x02\x02\x02"+ + "\'\x8C\x03\x02\x02\x02)\x91\x03\x02\x02\x02+,\x07p\x02\x02,-\x07q\x02"+ + "\x02-.\x07o\x02\x02./\x07k\x02\x02/0\x07p\x02\x0201\x07c\x02\x0212\x07"+ + "v\x02\x0223\x07g\x02\x0234\x07u\x02\x024\x04\x03\x02\x02\x0256\x07t\x02"+ + "\x0267\x07g\x02\x0278\x07n\x02\x0289\x07g\x02\x029:\x07c\x02\x02:;\x07"+ + "u\x02\x02;<\x07g\x02\x02<=\x07u\x02\x02=\x06\x03\x02\x02\x02>?\x07u\x02"+ + "\x02?@\x07g\x02\x02@A\x07n\x02\x02AB\x07h\x02\x02B\b\x03\x02\x02\x02C"+ + "D\x07g\x02\x02DE\x07p\x02\x02EF\x07f\x02\x02FG\x07q\x02\x02GH\x07t\x02"+ + "\x02HI\x07u\x02\x02IJ\x07g\x02\x02JK\x07f\x02\x02KL\x07/\x02\x02LM\x07"+ + "d\x02\x02MX\x07{\x02\x02NO\x07g\x02\x02OP\x07p\x02\x02PQ\x07f\x02\x02"+ + "QR\x07q\x02\x02RS\x07t\x02\x02ST\x07u\x02\x02TU\x07g\x02\x02UV\x07t\x02"+ + "\x02VX\x07u\x02\x02WC\x03\x02\x02\x02WN\x03\x02\x02\x02X\n\x03\x02\x02"+ + "\x02YZ\x07e\x02\x02Z[\x07c\x02\x02[\\\x07u\x02\x02\\]\x07g\x02\x02]^\x07"+ + "/\x02\x02^_\x07e\x02\x02_`\x07t\x02\x02`a\x07g\x02\x02ab\x07c\x02\x02"+ + "bc\x07v\x02\x02cd\x07q\x02\x02de\x07t\x02\x02e\f\x03\x02\x02\x02fg\x07"+ + "c\x02\x02gh\x07p\x02\x02hi\x07f\x02\x02i\x0E\x03\x02\x02\x02jk\x07q\x02"+ + "\x02kl\x07t\x02\x02l\x10\x03\x02\x02\x02mn\x07k\x02\x02no\x07u\x02\x02"+ + "o\x12\x03\x02\x02\x02pq\x07k\x02\x02qr\x07p\x02\x02r\x14\x03\x02\x02\x02"+ + "st\x07p\x02\x02tu\x07q\x02\x02uv\x07v\x02\x02v\x16\x03\x02\x02\x02wx\x07"+ + "W\x02\x02xy\x07p\x02\x02yz\x07f\x02\x02z{\x07g\x02\x02{|\x07t\x02\x02"+ + "|\x18\x03\x02\x02\x02}~\x07.\x02\x02~\x1A\x03\x02\x02\x02\x7F\x80\x07"+ + "0\x02\x02\x80\x1C\x03\x02\x02\x02\x81\x82\x07=\x02\x02\x82\x1E\x03\x02"+ + "\x02\x02\x83\x84\x07*\x02\x02\x84 \x03\x02\x02\x02\x85\x86\x07+\x02\x02"+ + "\x86\"\x03\x02\x02\x02\x87\x88\x07}\x02\x02\x88$\x03\x02\x02\x02\x89\x8A"+ + "\x07\x7F\x02\x02\x8A&\x03\x02\x02\x02\x8B\x8D\t\x02\x02\x02\x8C\x8B\x03"+ + "\x02\x02\x02\x8D\x8E\x03\x02\x02\x02\x8E\x8C\x03\x02\x02\x02\x8E\x8F\x03"+ + "\x02\x02\x02\x8F(\x03\x02\x02\x02\x90\x92\t\x03\x02\x02\x91\x90\x03\x02"+ + "\x02\x02\x92\x93\x03\x02\x02\x02\x93\x91\x03\x02\x02\x02\x93\x94\x03\x02"+ + "\x02\x02\x94\x95\x03\x02\x02\x02\x95\x96\b\x15\x02\x02\x96*\x03\x02\x02"+ + "\x02\x06\x02W\x8E\x93\x03\b\x02\x02"; + public static __ATN: ATN; + public static get _ATN(): ATN { + if (!binding_grammarLexer.__ATN) { + binding_grammarLexer.__ATN = new ATNDeserializer().deserialize(Utils.toCharArray(binding_grammarLexer._serializedATN)); + } + + return binding_grammarLexer.__ATN; + } + +} + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarListener.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarListener.ts new file mode 100644 index 0000000..4324152 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarListener.ts @@ -0,0 +1,217 @@ +// Generated from ./src/models/dynamic_binding/antlr/binding_grammar.g4 by ANTLR 4.6-SNAPSHOT + + +import { ParseTreeListener } from 'antlr4ts/tree/ParseTreeListener'; + +import { Binding_policyContext } from './binding_grammarParser'; +import { Binding_setContext } from './binding_grammarParser'; +import { Unbinding_setContext } from './binding_grammarParser'; +import { Binding_statementContext } from './binding_grammarParser'; +import { Unbinding_statementContext } from './binding_grammarParser'; +import { Is_creatorContext } from './binding_grammarParser'; +import { Binding_constrContext } from './binding_grammarParser'; +import { Endorsement_constrContext } from './binding_grammarParser'; +import { Set_expresionContext } from './binding_grammarParser'; +import { Scope_restrictionContext } from './binding_grammarParser'; +import { NominatorContext } from './binding_grammarParser'; +import { NomineeContext } from './binding_grammarParser'; +import { RoleContext } from './binding_grammarParser'; +import { Role_path_expresionContext } from './binding_grammarParser'; +import { Subprocess_idContext } from './binding_grammarParser'; +import { Role_idContext } from './binding_grammarParser'; +import { Task_idContext } from './binding_grammarParser'; + + +/** + * This interface defines a complete listener for a parse tree produced by + * `binding_grammarParser`. + */ +export interface binding_grammarListener extends ParseTreeListener { + /** + * Enter a parse tree produced by `binding_grammarParser.binding_policy`. + * @param ctx the parse tree + */ + enterBinding_policy?: (ctx: Binding_policyContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.binding_policy`. + * @param ctx the parse tree + */ + exitBinding_policy?: (ctx: Binding_policyContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.binding_set`. + * @param ctx the parse tree + */ + enterBinding_set?: (ctx: Binding_setContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.binding_set`. + * @param ctx the parse tree + */ + exitBinding_set?: (ctx: Binding_setContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.unbinding_set`. + * @param ctx the parse tree + */ + enterUnbinding_set?: (ctx: Unbinding_setContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.unbinding_set`. + * @param ctx the parse tree + */ + exitUnbinding_set?: (ctx: Unbinding_setContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.binding_statement`. + * @param ctx the parse tree + */ + enterBinding_statement?: (ctx: Binding_statementContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.binding_statement`. + * @param ctx the parse tree + */ + exitBinding_statement?: (ctx: Binding_statementContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.unbinding_statement`. + * @param ctx the parse tree + */ + enterUnbinding_statement?: (ctx: Unbinding_statementContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.unbinding_statement`. + * @param ctx the parse tree + */ + exitUnbinding_statement?: (ctx: Unbinding_statementContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.is_creator`. + * @param ctx the parse tree + */ + enterIs_creator?: (ctx: Is_creatorContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.is_creator`. + * @param ctx the parse tree + */ + exitIs_creator?: (ctx: Is_creatorContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.binding_constr`. + * @param ctx the parse tree + */ + enterBinding_constr?: (ctx: Binding_constrContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.binding_constr`. + * @param ctx the parse tree + */ + exitBinding_constr?: (ctx: Binding_constrContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.endorsement_constr`. + * @param ctx the parse tree + */ + enterEndorsement_constr?: (ctx: Endorsement_constrContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.endorsement_constr`. + * @param ctx the parse tree + */ + exitEndorsement_constr?: (ctx: Endorsement_constrContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.set_expresion`. + * @param ctx the parse tree + */ + enterSet_expresion?: (ctx: Set_expresionContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.set_expresion`. + * @param ctx the parse tree + */ + exitSet_expresion?: (ctx: Set_expresionContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.scope_restriction`. + * @param ctx the parse tree + */ + enterScope_restriction?: (ctx: Scope_restrictionContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.scope_restriction`. + * @param ctx the parse tree + */ + exitScope_restriction?: (ctx: Scope_restrictionContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.nominator`. + * @param ctx the parse tree + */ + enterNominator?: (ctx: NominatorContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.nominator`. + * @param ctx the parse tree + */ + exitNominator?: (ctx: NominatorContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.nominee`. + * @param ctx the parse tree + */ + enterNominee?: (ctx: NomineeContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.nominee`. + * @param ctx the parse tree + */ + exitNominee?: (ctx: NomineeContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.role`. + * @param ctx the parse tree + */ + enterRole?: (ctx: RoleContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.role`. + * @param ctx the parse tree + */ + exitRole?: (ctx: RoleContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.role_path_expresion`. + * @param ctx the parse tree + */ + enterRole_path_expresion?: (ctx: Role_path_expresionContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.role_path_expresion`. + * @param ctx the parse tree + */ + exitRole_path_expresion?: (ctx: Role_path_expresionContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.subprocess_id`. + * @param ctx the parse tree + */ + enterSubprocess_id?: (ctx: Subprocess_idContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.subprocess_id`. + * @param ctx the parse tree + */ + exitSubprocess_id?: (ctx: Subprocess_idContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.role_id`. + * @param ctx the parse tree + */ + enterRole_id?: (ctx: Role_idContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.role_id`. + * @param ctx the parse tree + */ + exitRole_id?: (ctx: Role_idContext) => void; + + /** + * Enter a parse tree produced by `binding_grammarParser.task_id`. + * @param ctx the parse tree + */ + enterTask_id?: (ctx: Task_idContext) => void; + /** + * Exit a parse tree produced by `binding_grammarParser.task_id`. + * @param ctx the parse tree + */ + exitTask_id?: (ctx: Task_idContext) => void; +} + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarParser.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarParser.ts new file mode 100644 index 0000000..c3a6c24 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarParser.ts @@ -0,0 +1,1386 @@ +// Generated from ./src/models/dynamic_binding/antlr/binding_grammar.g4 by ANTLR 4.6-SNAPSHOT + + +import { ATN } from 'antlr4ts/atn/ATN'; +import { ATNDeserializer } from 'antlr4ts/atn/ATNDeserializer'; +import { FailedPredicateException } from 'antlr4ts/FailedPredicateException'; +import { NotNull } from 'antlr4ts/Decorators'; +import { NoViableAltException } from 'antlr4ts/NoViableAltException'; +import { Override } from 'antlr4ts/Decorators'; +import { Parser } from 'antlr4ts/Parser'; +import { ParserRuleContext } from 'antlr4ts/ParserRuleContext'; +import { ParserATNSimulator } from 'antlr4ts/atn/ParserATNSimulator'; +import { ParseTreeListener } from 'antlr4ts/tree/ParseTreeListener'; +import { ParseTreeVisitor } from 'antlr4ts/tree/ParseTreeVisitor'; +import { RecognitionException } from 'antlr4ts/RecognitionException'; +import { RuleContext } from 'antlr4ts/RuleContext'; +import { RuleVersion } from 'antlr4ts/RuleVersion'; +import { TerminalNode } from 'antlr4ts/tree/TerminalNode'; +import { Token } from 'antlr4ts/Token'; +import { TokenStream } from 'antlr4ts/TokenStream'; +import { Vocabulary } from 'antlr4ts/Vocabulary'; +import { VocabularyImpl } from 'antlr4ts/VocabularyImpl'; + +import * as Utils from 'antlr4ts/misc/Utils'; + +import { binding_grammarListener } from './binding_grammarListener'; +import { binding_grammarVisitor } from './binding_grammarVisitor'; + + +export class binding_grammarParser extends Parser { + public static readonly NOMINATES=1; + public static readonly RELEASES=2; + public static readonly SELF=3; + public static readonly ENDORSED_BY=4; + public static readonly CASE_CREATOR=5; + public static readonly AND=6; + public static readonly OR=7; + public static readonly IS=8; + public static readonly IN=9; + public static readonly NOT=10; + public static readonly UNDER=11; + public static readonly COMMA=12; + public static readonly DOT=13; + public static readonly SEMICOLON=14; + public static readonly LPAREN=15; + public static readonly RPAREN=16; + public static readonly LBRACES=17; + public static readonly RBRACES=18; + public static readonly IDENTIFIER=19; + public static readonly WS=20; + public static readonly RULE_binding_policy = 0; + public static readonly RULE_binding_set = 1; + public static readonly RULE_unbinding_set = 2; + public static readonly RULE_binding_statement = 3; + public static readonly RULE_unbinding_statement = 4; + public static readonly RULE_is_creator = 5; + public static readonly RULE_binding_constr = 6; + public static readonly RULE_endorsement_constr = 7; + public static readonly RULE_set_expresion = 8; + public static readonly RULE_scope_restriction = 9; + public static readonly RULE_nominator = 10; + public static readonly RULE_nominee = 11; + public static readonly RULE_role = 12; + public static readonly RULE_role_path_expresion = 13; + public static readonly RULE_subprocess_id = 14; + public static readonly RULE_role_id = 15; + public static readonly RULE_task_id = 16; + public static readonly ruleNames: string[] = [ + "binding_policy", "binding_set", "unbinding_set", "binding_statement", + "unbinding_statement", "is_creator", "binding_constr", "endorsement_constr", + "set_expresion", "scope_restriction", "nominator", "nominee", "role", + "role_path_expresion", "subprocess_id", "role_id", "task_id" + ]; + + private static readonly _LITERAL_NAMES: (string | undefined)[] = [ + undefined, "'nominates'", "'releases'", "'self'", undefined, "'case-creator'", + "'and'", "'or'", "'is'", "'in'", "'not'", "'Under'", "','", "'.'", "';'", + "'('", "')'", "'{'", "'}'" + ]; + private static readonly _SYMBOLIC_NAMES: (string | undefined)[] = [ + undefined, "NOMINATES", "RELEASES", "SELF", "ENDORSED_BY", "CASE_CREATOR", + "AND", "OR", "IS", "IN", "NOT", "UNDER", "COMMA", "DOT", "SEMICOLON", + "LPAREN", "RPAREN", "LBRACES", "RBRACES", "IDENTIFIER", "WS" + ]; + public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(binding_grammarParser._LITERAL_NAMES, binding_grammarParser._SYMBOLIC_NAMES, []); + + @Override + @NotNull + public get vocabulary(): Vocabulary { + return binding_grammarParser.VOCABULARY; + } + + @Override + public get grammarFileName(): string { return "binding_grammar.g4"; } + + @Override + public get ruleNames(): string[] { return binding_grammarParser.ruleNames; } + + @Override + public get serializedATN(): string { return binding_grammarParser._serializedATN; } + + constructor(input: TokenStream) { + super(input); + this._interp = new ParserATNSimulator(binding_grammarParser._ATN, this); + } + @RuleVersion(0) + public binding_policy(): Binding_policyContext { + let _localctx: Binding_policyContext = new Binding_policyContext(this._ctx, this.state); + this.enterRule(_localctx, 0, binding_grammarParser.RULE_binding_policy); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 34; + this.binding_set(); + this.state = 35; + this.unbinding_set(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public binding_set(): Binding_setContext { + let _localctx: Binding_setContext = new Binding_setContext(this._ctx, this.state); + this.enterRule(_localctx, 2, binding_grammarParser.RULE_binding_set); + let _la: number; + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 37; + this.match(binding_grammarParser.LBRACES); + this.state = 38; + this.binding_statement(); + this.state = 43; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===binding_grammarParser.SEMICOLON) { + { + { + this.state = 39; + this.match(binding_grammarParser.SEMICOLON); + this.state = 40; + this.binding_statement(); + } + } + this.state = 45; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + this.state = 46; + this.match(binding_grammarParser.RBRACES); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public unbinding_set(): Unbinding_setContext { + let _localctx: Unbinding_setContext = new Unbinding_setContext(this._ctx, this.state); + this.enterRule(_localctx, 4, binding_grammarParser.RULE_unbinding_set); + let _la: number; + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 48; + this.match(binding_grammarParser.LBRACES); + this.state = 58; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case binding_grammarParser.SELF: + case binding_grammarParser.IDENTIFIER: + { + this.state = 49; + this.unbinding_statement(); + this.state = 54; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la===binding_grammarParser.SEMICOLON) { + { + { + this.state = 50; + this.match(binding_grammarParser.SEMICOLON); + this.state = 51; + this.unbinding_statement(); + } + } + this.state = 56; + this._errHandler.sync(this); + _la = this._input.LA(1); + } + } + break; + case binding_grammarParser.RBRACES: + { + } + break; + default: + throw new NoViableAltException(this); + } + this.state = 60; + this.match(binding_grammarParser.RBRACES); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public binding_statement(): Binding_statementContext { + let _localctx: Binding_statementContext = new Binding_statementContext(this._ctx, this.state); + this.enterRule(_localctx, 6, binding_grammarParser.RULE_binding_statement); + let _la: number; + try { + this.state = 75; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input,6,this._ctx) ) { + case 1: + this.enterOuterAlt(_localctx, 1); + { + this.state = 62; + this.is_creator(); + } + break; + + case 2: + this.enterOuterAlt(_localctx, 2); + { + this.state = 64; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===binding_grammarParser.UNDER) { + { + this.state = 63; + this.scope_restriction(); + } + } + + this.state = 66; + this.nominator(); + this.state = 67; + this.match(binding_grammarParser.NOMINATES); + this.state = 68; + this.nominee(); + this.state = 70; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===binding_grammarParser.IN || _la===binding_grammarParser.NOT) { + { + this.state = 69; + this.binding_constr(); + } + } + + this.state = 73; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===binding_grammarParser.ENDORSED_BY) { + { + this.state = 72; + this.endorsement_constr(); + } + } + + } + break; + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public unbinding_statement(): Unbinding_statementContext { + let _localctx: Unbinding_statementContext = new Unbinding_statementContext(this._ctx, this.state); + this.enterRule(_localctx, 8, binding_grammarParser.RULE_unbinding_statement); + let _la: number; + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 77; + this.nominator(); + this.state = 78; + this.match(binding_grammarParser.RELEASES); + this.state = 79; + this.nominee(); + this.state = 81; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===binding_grammarParser.IN || _la===binding_grammarParser.NOT) { + { + this.state = 80; + this.binding_constr(); + } + } + + this.state = 84; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la===binding_grammarParser.ENDORSED_BY) { + { + this.state = 83; + this.endorsement_constr(); + } + } + + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public is_creator(): Is_creatorContext { + let _localctx: Is_creatorContext = new Is_creatorContext(this._ctx, this.state); + this.enterRule(_localctx, 10, binding_grammarParser.RULE_is_creator); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 86; + this.role(); + this.state = 87; + this.match(binding_grammarParser.IS); + this.state = 88; + this.match(binding_grammarParser.CASE_CREATOR); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public binding_constr(): Binding_constrContext { + let _localctx: Binding_constrContext = new Binding_constrContext(this._ctx, this.state); + this.enterRule(_localctx, 12, binding_grammarParser.RULE_binding_constr); + try { + this.state = 95; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case binding_grammarParser.NOT: + this.enterOuterAlt(_localctx, 1); + { + this.state = 90; + this.match(binding_grammarParser.NOT); + this.state = 91; + this.match(binding_grammarParser.IN); + this.state = 92; + this.set_expresion(); + } + break; + case binding_grammarParser.IN: + this.enterOuterAlt(_localctx, 2); + { + this.state = 93; + this.match(binding_grammarParser.IN); + this.state = 94; + this.set_expresion(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public endorsement_constr(): Endorsement_constrContext { + let _localctx: Endorsement_constrContext = new Endorsement_constrContext(this._ctx, this.state); + this.enterRule(_localctx, 14, binding_grammarParser.RULE_endorsement_constr); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 97; + this.match(binding_grammarParser.ENDORSED_BY); + this.state = 98; + this.set_expresion(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public set_expresion(): Set_expresionContext { + let _localctx: Set_expresionContext = new Set_expresionContext(this._ctx, this.state); + this.enterRule(_localctx, 16, binding_grammarParser.RULE_set_expresion); + try { + this.state = 113; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input,10,this._ctx) ) { + case 1: + this.enterOuterAlt(_localctx, 1); + { + this.state = 100; + this.match(binding_grammarParser.LPAREN); + this.state = 101; + this.set_expresion(); + this.state = 102; + this.match(binding_grammarParser.RPAREN); + } + break; + + case 2: + this.enterOuterAlt(_localctx, 2); + { + this.state = 104; + this.role(); + this.state = 105; + this.match(binding_grammarParser.OR); + this.state = 106; + this.set_expresion(); + } + break; + + case 3: + this.enterOuterAlt(_localctx, 3); + { + this.state = 108; + this.role(); + this.state = 109; + this.match(binding_grammarParser.AND); + this.state = 110; + this.set_expresion(); + } + break; + + case 4: + this.enterOuterAlt(_localctx, 4); + { + this.state = 112; + this.role(); + } + break; + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public scope_restriction(): Scope_restrictionContext { + let _localctx: Scope_restrictionContext = new Scope_restrictionContext(this._ctx, this.state); + this.enterRule(_localctx, 18, binding_grammarParser.RULE_scope_restriction); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 115; + this.match(binding_grammarParser.UNDER); + this.state = 116; + this.subprocess_id(); + this.state = 117; + this.match(binding_grammarParser.COMMA); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public nominator(): NominatorContext { + let _localctx: NominatorContext = new NominatorContext(this._ctx, this.state); + this.enterRule(_localctx, 20, binding_grammarParser.RULE_nominator); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 119; + this.role(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public nominee(): NomineeContext { + let _localctx: NomineeContext = new NomineeContext(this._ctx, this.state); + this.enterRule(_localctx, 22, binding_grammarParser.RULE_nominee); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 121; + this.role(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public role(): RoleContext { + let _localctx: RoleContext = new RoleContext(this._ctx, this.state); + this.enterRule(_localctx, 24, binding_grammarParser.RULE_role); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 126; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input,11,this._ctx) ) { + case 1: + { + this.state = 123; + this.role_id(); + } + break; + + case 2: + { + this.state = 124; + this.role_path_expresion(); + } + break; + + case 3: + { + this.state = 125; + this.match(binding_grammarParser.SELF); + } + break; + } + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public role_path_expresion(): Role_path_expresionContext { + let _localctx: Role_path_expresionContext = new Role_path_expresionContext(this._ctx, this.state); + this.enterRule(_localctx, 26, binding_grammarParser.RULE_role_path_expresion); + try { + let _alt: number; + this.enterOuterAlt(_localctx, 1); + { + this.state = 131; + this._errHandler.sync(this); + _alt = 1; + do { + switch (_alt) { + case 1: + { + { + this.state = 128; + this.subprocess_id(); + this.state = 129; + this.match(binding_grammarParser.DOT); + } + } + break; + default: + throw new NoViableAltException(this); + } + this.state = 133; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input,12,this._ctx); + } while ( _alt!==2 && _alt!==ATN.INVALID_ALT_NUMBER ); + this.state = 135; + this.role_id(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public subprocess_id(): Subprocess_idContext { + let _localctx: Subprocess_idContext = new Subprocess_idContext(this._ctx, this.state); + this.enterRule(_localctx, 28, binding_grammarParser.RULE_subprocess_id); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 137; + this.match(binding_grammarParser.IDENTIFIER); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public role_id(): Role_idContext { + let _localctx: Role_idContext = new Role_idContext(this._ctx, this.state); + this.enterRule(_localctx, 30, binding_grammarParser.RULE_role_id); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 139; + this.match(binding_grammarParser.IDENTIFIER); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + @RuleVersion(0) + public task_id(): Task_idContext { + let _localctx: Task_idContext = new Task_idContext(this._ctx, this.state); + this.enterRule(_localctx, 32, binding_grammarParser.RULE_task_id); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 141; + this.match(binding_grammarParser.IDENTIFIER); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + + public static readonly _serializedATN: string = + "\x03\uAF6F\u8320\u479D\uB75C\u4880\u1605\u191C\uAB37\x03\x16\x92\x04\x02"+ + "\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06\x04\x07"+ + "\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r\t\r\x04"+ + "\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t\x12\x03"+ + "\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x07\x03,\n\x03\f"+ + "\x03\x0E\x03/\v\x03\x03\x03\x03\x03\x03\x04\x03\x04\x03\x04\x03\x04\x07"+ + "\x047\n\x04\f\x04\x0E\x04:\v\x04\x03\x04\x05\x04=\n\x04\x03\x04\x03\x04"+ + "\x03\x05\x03\x05\x05\x05C\n\x05\x03\x05\x03\x05\x03\x05\x03\x05\x05\x05"+ + "I\n\x05\x03\x05\x05\x05L\n\x05\x05\x05N\n\x05\x03\x06\x03\x06\x03\x06"+ + "\x03\x06\x05\x06T\n\x06\x03\x06\x05\x06W\n\x06\x03\x07\x03\x07\x03\x07"+ + "\x03\x07\x03\b\x03\b\x03\b\x03\b\x03\b\x05\bb\n\b\x03\t\x03\t\x03\t\x03"+ + "\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03"+ + "\n\x05\nt\n\n\x03\v\x03\v\x03\v\x03\v\x03\f\x03\f\x03\r\x03\r\x03\x0E"+ + "\x03\x0E\x03\x0E\x05\x0E\x81\n\x0E\x03\x0F\x03\x0F\x03\x0F\x06\x0F\x86"+ + "\n\x0F\r\x0F\x0E\x0F\x87\x03\x0F\x03\x0F\x03\x10\x03\x10\x03\x11\x03\x11"+ + "\x03\x12\x03\x12\x03\x12\x02\x02\x02\x13\x02\x02\x04\x02\x06\x02\b\x02"+ + "\n\x02\f\x02\x0E\x02\x10\x02\x12\x02\x14\x02\x16\x02\x18\x02\x1A\x02\x1C"+ + "\x02\x1E\x02 \x02\"\x02\x02\x02\x90\x02$\x03\x02\x02\x02\x04\'\x03\x02"+ + "\x02\x02\x062\x03\x02\x02\x02\bM\x03\x02\x02\x02\nO\x03\x02\x02\x02\f"+ + "X\x03\x02\x02\x02\x0Ea\x03\x02\x02\x02\x10c\x03\x02\x02\x02\x12s\x03\x02"+ + "\x02\x02\x14u\x03\x02\x02\x02\x16y\x03\x02\x02\x02\x18{\x03\x02\x02\x02"+ + "\x1A\x80\x03\x02\x02\x02\x1C\x85\x03\x02\x02\x02\x1E\x8B\x03\x02\x02\x02"+ + " \x8D\x03\x02\x02\x02\"\x8F\x03\x02\x02\x02$%\x05\x04\x03\x02%&\x05\x06"+ + "\x04\x02&\x03\x03\x02\x02\x02\'(\x07\x13\x02\x02(-\x05\b\x05\x02)*\x07"+ + "\x10\x02\x02*,\x05\b\x05\x02+)\x03\x02\x02\x02,/\x03\x02\x02\x02-+\x03"+ + "\x02\x02\x02-.\x03\x02\x02\x02.0\x03\x02\x02\x02/-\x03\x02\x02\x0201\x07"+ + "\x14\x02\x021\x05\x03\x02\x02\x022<\x07\x13\x02\x0238\x05\n\x06\x0245"+ + "\x07\x10\x02\x0257\x05\n\x06\x0264\x03\x02\x02\x027:\x03\x02\x02\x028"+ + "6\x03\x02\x02\x0289\x03\x02\x02\x029=\x03\x02\x02\x02:8\x03\x02\x02\x02"+ + ";=\x03\x02\x02\x02<3\x03\x02\x02\x02<;\x03\x02\x02\x02=>\x03\x02\x02\x02"+ + ">?\x07\x14\x02\x02?\x07\x03\x02\x02\x02@N\x05\f\x07\x02AC\x05\x14\v\x02"+ + "BA\x03\x02\x02\x02BC\x03\x02\x02\x02CD\x03\x02\x02\x02DE\x05\x16\f\x02"+ + "EF\x07\x03\x02\x02FH\x05\x18\r\x02GI\x05\x0E\b\x02HG\x03\x02\x02\x02H"+ + "I\x03\x02\x02\x02IK\x03\x02\x02\x02JL\x05\x10\t\x02KJ\x03\x02\x02\x02"+ + "KL\x03\x02\x02\x02LN\x03\x02\x02\x02M@\x03\x02\x02\x02MB\x03\x02\x02\x02"+ + "N\t\x03\x02\x02\x02OP\x05\x16\f\x02PQ\x07\x04\x02\x02QS\x05\x18\r\x02"+ + "RT\x05\x0E\b\x02SR\x03\x02\x02\x02ST\x03\x02\x02\x02TV\x03\x02\x02\x02"+ + "UW\x05\x10\t\x02VU\x03\x02\x02\x02VW\x03\x02\x02\x02W\v\x03\x02\x02\x02"+ + "XY\x05\x1A\x0E\x02YZ\x07\n\x02\x02Z[\x07\x07\x02\x02[\r\x03\x02\x02\x02"+ + "\\]\x07\f\x02\x02]^\x07\v\x02\x02^b\x05\x12\n\x02_`\x07\v\x02\x02`b\x05"+ + "\x12\n\x02a\\\x03\x02\x02\x02a_\x03\x02\x02\x02b\x0F\x03\x02\x02\x02c"+ + "d\x07\x06\x02\x02de\x05\x12\n\x02e\x11\x03\x02\x02\x02fg\x07\x11\x02\x02"+ + "gh\x05\x12\n\x02hi\x07\x12\x02\x02it\x03\x02\x02\x02jk\x05\x1A\x0E\x02"+ + "kl\x07\t\x02\x02lm\x05\x12\n\x02mt\x03\x02\x02\x02no\x05\x1A\x0E\x02o"+ + "p\x07\b\x02\x02pq\x05\x12\n\x02qt\x03\x02\x02\x02rt\x05\x1A\x0E\x02sf"+ + "\x03\x02\x02\x02sj\x03\x02\x02\x02sn\x03\x02\x02\x02sr\x03\x02\x02\x02"+ + "t\x13\x03\x02\x02\x02uv\x07\r\x02\x02vw\x05\x1E\x10\x02wx\x07\x0E\x02"+ + "\x02x\x15\x03\x02\x02\x02yz\x05\x1A\x0E\x02z\x17\x03\x02\x02\x02{|\x05"+ + "\x1A\x0E\x02|\x19\x03\x02\x02\x02}\x81\x05 \x11\x02~\x81\x05\x1C\x0F\x02"+ + "\x7F\x81\x07\x05\x02\x02\x80}\x03\x02\x02\x02\x80~\x03\x02\x02\x02\x80"+ + "\x7F\x03\x02\x02\x02\x81\x1B\x03\x02\x02\x02\x82\x83\x05\x1E\x10\x02\x83"+ + "\x84\x07\x0F\x02\x02\x84\x86\x03\x02\x02\x02\x85\x82\x03\x02\x02\x02\x86"+ + "\x87\x03\x02\x02\x02\x87\x85\x03\x02\x02\x02\x87\x88\x03\x02\x02\x02\x88"+ + "\x89\x03\x02\x02\x02\x89\x8A\x05 \x11\x02\x8A\x1D\x03\x02\x02\x02\x8B"+ + "\x8C\x07\x15\x02\x02\x8C\x1F\x03\x02\x02\x02\x8D\x8E\x07\x15\x02\x02\x8E"+ + "!\x03\x02\x02\x02\x8F\x90\x07\x15\x02\x02\x90#\x03\x02\x02\x02\x0F-8<"+ + "BHKMSVas\x80\x87"; + public static __ATN: ATN; + public static get _ATN(): ATN { + if (!binding_grammarParser.__ATN) { + binding_grammarParser.__ATN = new ATNDeserializer().deserialize(Utils.toCharArray(binding_grammarParser._serializedATN)); + } + + return binding_grammarParser.__ATN; + } + +} + +export class Binding_policyContext extends ParserRuleContext { + public binding_set(): Binding_setContext { + return this.getRuleContext(0, Binding_setContext); + } + public unbinding_set(): Unbinding_setContext { + return this.getRuleContext(0, Unbinding_setContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_binding_policy; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterBinding_policy) listener.enterBinding_policy(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitBinding_policy) listener.exitBinding_policy(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitBinding_policy) return visitor.visitBinding_policy(this); + else return visitor.visitChildren(this); + } +} + + +export class Binding_setContext extends ParserRuleContext { + public LBRACES(): TerminalNode { return this.getToken(binding_grammarParser.LBRACES, 0); } + public binding_statement(): Binding_statementContext[]; + public binding_statement(i: number): Binding_statementContext; + public binding_statement(i?: number): Binding_statementContext | Binding_statementContext[] { + if (i === undefined) { + return this.getRuleContexts(Binding_statementContext); + } else { + return this.getRuleContext(i, Binding_statementContext); + } + } + public RBRACES(): TerminalNode { return this.getToken(binding_grammarParser.RBRACES, 0); } + public SEMICOLON(): TerminalNode[]; + public SEMICOLON(i: number): TerminalNode; + public SEMICOLON(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(binding_grammarParser.SEMICOLON); + } else { + return this.getToken(binding_grammarParser.SEMICOLON, i); + } + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_binding_set; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterBinding_set) listener.enterBinding_set(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitBinding_set) listener.exitBinding_set(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitBinding_set) return visitor.visitBinding_set(this); + else return visitor.visitChildren(this); + } +} + + +export class Unbinding_setContext extends ParserRuleContext { + public LBRACES(): TerminalNode { return this.getToken(binding_grammarParser.LBRACES, 0); } + public RBRACES(): TerminalNode { return this.getToken(binding_grammarParser.RBRACES, 0); } + public unbinding_statement(): Unbinding_statementContext[]; + public unbinding_statement(i: number): Unbinding_statementContext; + public unbinding_statement(i?: number): Unbinding_statementContext | Unbinding_statementContext[] { + if (i === undefined) { + return this.getRuleContexts(Unbinding_statementContext); + } else { + return this.getRuleContext(i, Unbinding_statementContext); + } + } + public SEMICOLON(): TerminalNode[]; + public SEMICOLON(i: number): TerminalNode; + public SEMICOLON(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(binding_grammarParser.SEMICOLON); + } else { + return this.getToken(binding_grammarParser.SEMICOLON, i); + } + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_unbinding_set; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterUnbinding_set) listener.enterUnbinding_set(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitUnbinding_set) listener.exitUnbinding_set(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitUnbinding_set) return visitor.visitUnbinding_set(this); + else return visitor.visitChildren(this); + } +} + + +export class Binding_statementContext extends ParserRuleContext { + public is_creator(): Is_creatorContext | undefined { + return this.tryGetRuleContext(0, Is_creatorContext); + } + public nominator(): NominatorContext | undefined { + return this.tryGetRuleContext(0, NominatorContext); + } + public NOMINATES(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.NOMINATES, 0); } + public nominee(): NomineeContext | undefined { + return this.tryGetRuleContext(0, NomineeContext); + } + public scope_restriction(): Scope_restrictionContext | undefined { + return this.tryGetRuleContext(0, Scope_restrictionContext); + } + public binding_constr(): Binding_constrContext | undefined { + return this.tryGetRuleContext(0, Binding_constrContext); + } + public endorsement_constr(): Endorsement_constrContext | undefined { + return this.tryGetRuleContext(0, Endorsement_constrContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_binding_statement; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterBinding_statement) listener.enterBinding_statement(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitBinding_statement) listener.exitBinding_statement(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitBinding_statement) return visitor.visitBinding_statement(this); + else return visitor.visitChildren(this); + } +} + + +export class Unbinding_statementContext extends ParserRuleContext { + public nominator(): NominatorContext { + return this.getRuleContext(0, NominatorContext); + } + public RELEASES(): TerminalNode { return this.getToken(binding_grammarParser.RELEASES, 0); } + public nominee(): NomineeContext { + return this.getRuleContext(0, NomineeContext); + } + public binding_constr(): Binding_constrContext | undefined { + return this.tryGetRuleContext(0, Binding_constrContext); + } + public endorsement_constr(): Endorsement_constrContext | undefined { + return this.tryGetRuleContext(0, Endorsement_constrContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_unbinding_statement; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterUnbinding_statement) listener.enterUnbinding_statement(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitUnbinding_statement) listener.exitUnbinding_statement(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitUnbinding_statement) return visitor.visitUnbinding_statement(this); + else return visitor.visitChildren(this); + } +} + + +export class Is_creatorContext extends ParserRuleContext { + public role(): RoleContext { + return this.getRuleContext(0, RoleContext); + } + public IS(): TerminalNode { return this.getToken(binding_grammarParser.IS, 0); } + public CASE_CREATOR(): TerminalNode { return this.getToken(binding_grammarParser.CASE_CREATOR, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_is_creator; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterIs_creator) listener.enterIs_creator(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitIs_creator) listener.exitIs_creator(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitIs_creator) return visitor.visitIs_creator(this); + else return visitor.visitChildren(this); + } +} + + +export class Binding_constrContext extends ParserRuleContext { + public NOT(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.NOT, 0); } + public IN(): TerminalNode { return this.getToken(binding_grammarParser.IN, 0); } + public set_expresion(): Set_expresionContext { + return this.getRuleContext(0, Set_expresionContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_binding_constr; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterBinding_constr) listener.enterBinding_constr(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitBinding_constr) listener.exitBinding_constr(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitBinding_constr) return visitor.visitBinding_constr(this); + else return visitor.visitChildren(this); + } +} + + +export class Endorsement_constrContext extends ParserRuleContext { + public ENDORSED_BY(): TerminalNode { return this.getToken(binding_grammarParser.ENDORSED_BY, 0); } + public set_expresion(): Set_expresionContext { + return this.getRuleContext(0, Set_expresionContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_endorsement_constr; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterEndorsement_constr) listener.enterEndorsement_constr(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitEndorsement_constr) listener.exitEndorsement_constr(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitEndorsement_constr) return visitor.visitEndorsement_constr(this); + else return visitor.visitChildren(this); + } +} + + +export class Set_expresionContext extends ParserRuleContext { + public LPAREN(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.LPAREN, 0); } + public set_expresion(): Set_expresionContext | undefined { + return this.tryGetRuleContext(0, Set_expresionContext); + } + public RPAREN(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.RPAREN, 0); } + public role(): RoleContext | undefined { + return this.tryGetRuleContext(0, RoleContext); + } + public OR(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.OR, 0); } + public AND(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.AND, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_set_expresion; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterSet_expresion) listener.enterSet_expresion(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitSet_expresion) listener.exitSet_expresion(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitSet_expresion) return visitor.visitSet_expresion(this); + else return visitor.visitChildren(this); + } +} + + +export class Scope_restrictionContext extends ParserRuleContext { + public UNDER(): TerminalNode { return this.getToken(binding_grammarParser.UNDER, 0); } + public subprocess_id(): Subprocess_idContext { + return this.getRuleContext(0, Subprocess_idContext); + } + public COMMA(): TerminalNode { return this.getToken(binding_grammarParser.COMMA, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_scope_restriction; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterScope_restriction) listener.enterScope_restriction(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitScope_restriction) listener.exitScope_restriction(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitScope_restriction) return visitor.visitScope_restriction(this); + else return visitor.visitChildren(this); + } +} + + +export class NominatorContext extends ParserRuleContext { + public role(): RoleContext { + return this.getRuleContext(0, RoleContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_nominator; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterNominator) listener.enterNominator(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitNominator) listener.exitNominator(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitNominator) return visitor.visitNominator(this); + else return visitor.visitChildren(this); + } +} + + +export class NomineeContext extends ParserRuleContext { + public role(): RoleContext { + return this.getRuleContext(0, RoleContext); + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_nominee; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterNominee) listener.enterNominee(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitNominee) listener.exitNominee(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitNominee) return visitor.visitNominee(this); + else return visitor.visitChildren(this); + } +} + + +export class RoleContext extends ParserRuleContext { + public role_id(): Role_idContext | undefined { + return this.tryGetRuleContext(0, Role_idContext); + } + public role_path_expresion(): Role_path_expresionContext | undefined { + return this.tryGetRuleContext(0, Role_path_expresionContext); + } + public SELF(): TerminalNode | undefined { return this.tryGetToken(binding_grammarParser.SELF, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_role; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterRole) listener.enterRole(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitRole) listener.exitRole(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitRole) return visitor.visitRole(this); + else return visitor.visitChildren(this); + } +} + + +export class Role_path_expresionContext extends ParserRuleContext { + public role_id(): Role_idContext { + return this.getRuleContext(0, Role_idContext); + } + public subprocess_id(): Subprocess_idContext[]; + public subprocess_id(i: number): Subprocess_idContext; + public subprocess_id(i?: number): Subprocess_idContext | Subprocess_idContext[] { + if (i === undefined) { + return this.getRuleContexts(Subprocess_idContext); + } else { + return this.getRuleContext(i, Subprocess_idContext); + } + } + public DOT(): TerminalNode[]; + public DOT(i: number): TerminalNode; + public DOT(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(binding_grammarParser.DOT); + } else { + return this.getToken(binding_grammarParser.DOT, i); + } + } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_role_path_expresion; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterRole_path_expresion) listener.enterRole_path_expresion(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitRole_path_expresion) listener.exitRole_path_expresion(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitRole_path_expresion) return visitor.visitRole_path_expresion(this); + else return visitor.visitChildren(this); + } +} + + +export class Subprocess_idContext extends ParserRuleContext { + public IDENTIFIER(): TerminalNode { return this.getToken(binding_grammarParser.IDENTIFIER, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_subprocess_id; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterSubprocess_id) listener.enterSubprocess_id(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitSubprocess_id) listener.exitSubprocess_id(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitSubprocess_id) return visitor.visitSubprocess_id(this); + else return visitor.visitChildren(this); + } +} + + +export class Role_idContext extends ParserRuleContext { + public IDENTIFIER(): TerminalNode { return this.getToken(binding_grammarParser.IDENTIFIER, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_role_id; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterRole_id) listener.enterRole_id(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitRole_id) listener.exitRole_id(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitRole_id) return visitor.visitRole_id(this); + else return visitor.visitChildren(this); + } +} + + +export class Task_idContext extends ParserRuleContext { + public IDENTIFIER(): TerminalNode { return this.getToken(binding_grammarParser.IDENTIFIER, 0); } + constructor(parent: ParserRuleContext, invokingState: number); + constructor(parent: ParserRuleContext, invokingState: number) { + super(parent, invokingState); + + } + @Override public get ruleIndex(): number { return binding_grammarParser.RULE_task_id; } + @Override + public enterRule(listener: binding_grammarListener): void { + if (listener.enterTask_id) listener.enterTask_id(this); + } + @Override + public exitRule(listener: binding_grammarListener): void { + if (listener.exitTask_id) listener.exitTask_id(this); + } + @Override + public accept(visitor: binding_grammarVisitor): Result { + if (visitor.visitTask_id) return visitor.visitTask_id(this); + else return visitor.visitChildren(this); + } +} + + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarVisitor.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarVisitor.ts new file mode 100644 index 0000000..29e9ede --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/antlr/binding_grammarVisitor.ts @@ -0,0 +1,152 @@ +// Generated from ./src/models/dynamic_binding/antlr/binding_grammar.g4 by ANTLR 4.6-SNAPSHOT + + +import { ParseTreeVisitor } from 'antlr4ts/tree/ParseTreeVisitor'; + +import { Binding_policyContext } from './binding_grammarParser'; +import { Binding_setContext } from './binding_grammarParser'; +import { Unbinding_setContext } from './binding_grammarParser'; +import { Binding_statementContext } from './binding_grammarParser'; +import { Unbinding_statementContext } from './binding_grammarParser'; +import { Is_creatorContext } from './binding_grammarParser'; +import { Binding_constrContext } from './binding_grammarParser'; +import { Endorsement_constrContext } from './binding_grammarParser'; +import { Set_expresionContext } from './binding_grammarParser'; +import { Scope_restrictionContext } from './binding_grammarParser'; +import { NominatorContext } from './binding_grammarParser'; +import { NomineeContext } from './binding_grammarParser'; +import { RoleContext } from './binding_grammarParser'; +import { Role_path_expresionContext } from './binding_grammarParser'; +import { Subprocess_idContext } from './binding_grammarParser'; +import { Role_idContext } from './binding_grammarParser'; +import { Task_idContext } from './binding_grammarParser'; + + +/** + * This interface defines a complete generic visitor for a parse tree produced + * by `binding_grammarParser`. + * + * @param The return type of the visit operation. Use `void` for + * operations with no return type. + */ +export interface binding_grammarVisitor extends ParseTreeVisitor { + /** + * Visit a parse tree produced by `binding_grammarParser.binding_policy`. + * @param ctx the parse tree + * @return the visitor result + */ + visitBinding_policy?: (ctx: Binding_policyContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.binding_set`. + * @param ctx the parse tree + * @return the visitor result + */ + visitBinding_set?: (ctx: Binding_setContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.unbinding_set`. + * @param ctx the parse tree + * @return the visitor result + */ + visitUnbinding_set?: (ctx: Unbinding_setContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.binding_statement`. + * @param ctx the parse tree + * @return the visitor result + */ + visitBinding_statement?: (ctx: Binding_statementContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.unbinding_statement`. + * @param ctx the parse tree + * @return the visitor result + */ + visitUnbinding_statement?: (ctx: Unbinding_statementContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.is_creator`. + * @param ctx the parse tree + * @return the visitor result + */ + visitIs_creator?: (ctx: Is_creatorContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.binding_constr`. + * @param ctx the parse tree + * @return the visitor result + */ + visitBinding_constr?: (ctx: Binding_constrContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.endorsement_constr`. + * @param ctx the parse tree + * @return the visitor result + */ + visitEndorsement_constr?: (ctx: Endorsement_constrContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.set_expresion`. + * @param ctx the parse tree + * @return the visitor result + */ + visitSet_expresion?: (ctx: Set_expresionContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.scope_restriction`. + * @param ctx the parse tree + * @return the visitor result + */ + visitScope_restriction?: (ctx: Scope_restrictionContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.nominator`. + * @param ctx the parse tree + * @return the visitor result + */ + visitNominator?: (ctx: NominatorContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.nominee`. + * @param ctx the parse tree + * @return the visitor result + */ + visitNominee?: (ctx: NomineeContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.role`. + * @param ctx the parse tree + * @return the visitor result + */ + visitRole?: (ctx: RoleContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.role_path_expresion`. + * @param ctx the parse tree + * @return the visitor result + */ + visitRole_path_expresion?: (ctx: Role_path_expresionContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.subprocess_id`. + * @param ctx the parse tree + * @return the visitor result + */ + visitSubprocess_id?: (ctx: Subprocess_idContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.role_id`. + * @param ctx the parse tree + * @return the visitor result + */ + visitRole_id?: (ctx: Role_idContext) => Result; + + /** + * Visit a parse tree produced by `binding_grammarParser.task_id`. + * @param ctx the parse tree + * @return the visitor result + */ + visitTask_id?: (ctx: Task_idContext) => Result; +} + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol b/v3.0/caterpillar-core/src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol new file mode 100644 index 0000000..a641046 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol @@ -0,0 +1,207 @@ +pragma solidity ^0.4.25; + +// Function assertVote will return 0 -> UNBOUND, 1 -> NOMINATED, 2 -> RELEASING, 3 -> BOUND +contract BindingPolicy { + + function isCaseCreator(uint roleIndex) public pure returns(bool); + function canNominate (uint rNominator, uint rNominee) public pure returns(bool); + function assertNConstraint (uint rNominator, uint rNominee, uint nomineeRoles) public pure returns(bool); + function requireNEndorsement (uint rNominator, uint rNominee) public pure returns(bool); + function assertNVote (uint rNominator, uint rNominee, uint rEndorser, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(uint); + + function canRelease (uint rNominator, uint rNominee) public pure returns(bool); + function assertRConstraint (uint rNominator, uint rNominee, uint nomineeRoles) public pure returns(bool); + function requireREndorsement (uint rNominator, uint rNominee) public pure returns(bool); + function assertRVote (uint rNominator, uint rNominee, uint rEndorser, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(uint); +} + +contract ControlFlow { + function findParent() public view returns(address); + function getRoleFromTask(uint taskIndex, bytes32 processId) public pure returns(uint); + function bundleFor(address pCase) external view returns(bytes32); +} + + +contract BindingAccessControl { + + address private policyAdr; + address private taskRoleAdr; + address private registry; + + struct BindingInfo { + uint nominator; + uint nominee; + uint endorsedBy; + uint rejectedBy; + + // 0- UNBOUND, 1- RELEASING, 2- NOMINATED, 3- BOUND + uint bindingState; + } + + mapping (address => uint) private actorIndex; + uint private actorCount = 1; + + mapping (address => uint) private caseIndex; + uint private caseCount = 1; + + // caseIndex => roleIndex => actorIndex + mapping (uint => mapping (uint => BindingInfo)) private roleBindingState; + + // actorIndex => acceptedRoleIndexMask + mapping (uint => uint) private actorRoles; + + constructor(address _registry, address _policy, address _taskRoleAdr) public { + policyAdr = _policy; + taskRoleAdr = _taskRoleAdr; + registry = _registry; + } + + function roleState (uint role, address pCase) public view returns(uint) { + return roleBindingState[caseIndex[pCase]][role].bindingState; + } + + function nominateCaseCreator (uint rNominee, address nominee, address pCase) public { + + require(actorCount == 1 && BindingPolicy(policyAdr).isCaseCreator(rNominee)); + + uint creatorIndex = actorIndex[nominee] = actorCount++; + uint pCaseIndex = caseIndex[pCase] = caseCount++; + + roleBindingState[pCaseIndex][rNominee] = BindingInfo(creatorIndex, creatorIndex, 0, 0, 3); + actorRoles[creatorIndex] = uint(1) << rNominee; + + } + + function nominate (uint rNominator, uint rNominee, address nominator, address nominee, address pCase) public { + + // Verify that @processCase is a child in the process hierarchy where root is the process-case provided when nominated the process-creator. + // This verification can be done via the RuntimeRegistry in order to keep the consistency between control-flow, worklist and binding-control. + + uint iNominee = actorIndex[nominee]; + uint iCase = caseIndex[pCase]; + uint nomineeMask = uint(1) << rNominee; + uint iNominator = actorIndex[nominator]; + + // no actor can be BOUND/NOMINATED in pCase, the noinee cannot hold the rNominee role + // nominator actor must be binded to a role unless self-nomination is allowed + // the role of the nominator is allowed to nominate in the binding policy. + // the requested nomination fulfill the conditions defined in the policy by the instructions IN / NOT IN + require(roleBindingState[iCase][rNominee].bindingState == 0 && actorRoles[iNominee] & nomineeMask == 0 && + (actorRoles[iNominator] & (uint(1) << rNominator) != 0 || (iNominator == 0 && rNominator == rNominee)) && + (BindingPolicy(policyAdr).canNominate(rNominator, rNominee)) && + (BindingPolicy(policyAdr).assertNConstraint(rNominator, rNominee, actorRoles[iNominee]))); + + // At this point, all the conditions were fulfilled because otherwise an exception was thrown reverting the transaction. Thus the binding is allowed. + + // Verifying that all the indexes (for actors and process-cases) were already assigned to an index. + if(iCase == 0) + iCase = caseIndex[pCase] = caseCount++; + if(iNominee == 0) + iNominee = actorIndex[nominee] = actorCount++; + if(iNominator == 0) + iNominator = iNominee; + + // Binding the role and updating the state as NOMINATED(2) or BOUND(3) depending on the nomination require endorsement or not. + uint state = BindingPolicy(policyAdr).requireNEndorsement(rNominator, rNominee) ? 2 : 3; + + roleBindingState[iCase][rNominee] = BindingInfo (iNominator, iNominee, 0, 0, state); + if(state == 3) + actorRoles[iNominee] |= nomineeMask; + } + + function voteN (uint rNominator, uint rNominee, uint rEndorser, address endorser, address pCase, bool isAccepted) public returns(uint) { + //require(runtimeRegistry == msg.sender); + + uint iCase = caseIndex[pCase]; + BindingInfo memory roleVState = roleBindingState[iCase][rNominee]; + + // A nomination to endorse must be in NOMINATED state + // The endorser must be nominated before (state BOUND) unless the endorser is the nominee accepting the nomination. + require(roleVState.bindingState == 2 && + (actorRoles[actorIndex[endorser]] & (uint(1) << rEndorser) != 0 || rEndorser == rNominee && roleVState.nominee == actorIndex[endorser])); + + // This function is responsible to evaluate the endorsment. + // An exception reverting the transaction is thwown in case of any invalid operation (e.g wrong endorser). + // Thus, this function only returns if the endorsement is performed. + uint state = BindingPolicy(policyAdr).assertNVote(rNominator, rNominee, rEndorser, roleVState.endorsedBy, roleVState.rejectedBy, isAccepted); + + // The storage is only updated if the endorsment is valid and thus performed. + roleBindingState[iCase][rNominee].bindingState = state; + updateVoteMask(iCase, rNominee, uint(1) << rEndorser, isAccepted); + if (state == 0 || state == 3) { + roleBindingState[iCase][rNominee].endorsedBy = roleBindingState[iCase][rNominee].rejectedBy = 0; + if (state == 3) + actorRoles[roleVState.nominee] |= ((uint(1) << rNominee)); + } + return state; + } + + function release (uint rNominator, uint rNominee, address nominator, address pCase) public { + //require(runtimeRegistry == msg.sender); + + uint iCase = caseIndex[pCase]; + + BindingInfo memory roleRState = roleBindingState[iCase][rNominee]; + + // The endorser and the processCase address must be nominated before. + // Validating the rNomiator is allowed to release rNominee. + // Validating the release is not restricted by a binding constraint. + require(roleRState.bindingState == 3 && actorRoles[actorIndex[nominator]] & (uint(1) << rNominator) != 0 && + (BindingPolicy(policyAdr).canRelease(rNominator, rNominee)) && + (BindingPolicy(policyAdr).assertRConstraint(rNominator, rNominee, actorRoles[roleRState.nominee]))); + + // Unbinding the role and updating the state as UNBINDING or UNBINDED depending on the dismisses require endorsement or not. + if(!BindingPolicy(policyAdr).requireREndorsement(rNominator, rNominee)) { + roleBindingState[iCase][rNominee].bindingState = 0; + actorRoles[roleRState.nominee] &= ~(uint(1) << rNominee); + } + else + roleBindingState[iCase][rNominee].bindingState = 1; + } + + function voteR (uint rNominator, uint rNominee, uint rEndorser, address endorser, address pCase, bool isAccepted) public returns(uint) { + // require(runtimeRegistry == msg.sender); + + uint iCase = caseIndex[pCase]; + BindingInfo memory roleVRState = roleBindingState[iCase][rNominee]; + + // A release to vote must be in RELEASING state + // The endorsed involved in the vote and the processCase address must be nominated before. + require(roleVRState.bindingState == 1 && actorRoles[actorIndex[endorser]] & (uint(1) << rEndorser) != 0); + + // This function is responsible to evaluate the endorsment. + // An exception reverting the transaction is thwown in case of any invalid operation (e.g wrong endorser). + // Thus, that function only returns if the endorsement can be performed. + uint state = BindingPolicy(policyAdr).assertRVote(rNominator, rNominee, rEndorser, roleVRState.endorsedBy, roleVRState.rejectedBy, isAccepted); + + roleBindingState[iCase][rNominee].bindingState = state; + updateVoteMask(iCase, rNominee, uint(1) << rEndorser, isAccepted); + if (state == 0 || state == 3) { + roleBindingState[iCase][rNominee].endorsedBy = roleBindingState[iCase][rNominee].rejectedBy = 0; + if (state == 0) + actorRoles[roleVRState.nominee] &= ~(uint(1) << rNominee); + } + return state; + } + + + function canPerform(address actor, address pCase, uint taskIndex) public view returns(bool) { + bytes32 pId = ControlFlow(registry).bundleFor(pCase); + uint tRole = ControlFlow(taskRoleAdr).getRoleFromTask(taskIndex, pId); + uint iCase = caseIndex[pCase]; + while(roleBindingState[iCase][tRole].bindingState != 3) { + pCase = ControlFlow(pCase).findParent(); + if(pCase == 0) + break; + iCase = caseIndex[pCase]; + } + return actorIndex[actor] > 0 && roleBindingState[iCase][tRole].nominee == actorIndex[actor]; + } + + function updateVoteMask(uint iCase, uint rNominee, uint endorserMask, bool isAccepted) private { + if (isAccepted) + roleBindingState[iCase][rNominee].endorsedBy |= endorserMask; + else + roleBindingState[iCase][rNominee].rejectedBy |= endorserMask; + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyGenerator.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyGenerator.ts new file mode 100644 index 0000000..3d1744a --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyGenerator.ts @@ -0,0 +1,270 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as ejs from "ejs"; + +import { BigNumber } from "bignumber.js"; + +import antlr4 = require('antlr4/index'); + +import { ANTLRInputStream, CommonTokenStream, ANTLRErrorListener } from 'antlr4ts'; +import { binding_grammarLexer } from '../antlr/binding_grammarLexer'; +import { binding_grammarParser } from '../antlr/binding_grammarParser'; +import { BindingVisitor } from './BindingPolicyParser'; +import { Policy, DisjunctionSet, ConjunctionSet } from './DataStructures'; + + +const policy2solEJS = fs.readFileSync( + path.join(__dirname, "../../../../templates") + "/policy2sol.ejs", + "utf-8" +); + +let policy2solTemplate = ejs.compile(policy2solEJS); + +export let generatePolicy = (policyStr: string, policyName: string) => { + + return new Promise((resolve, reject) => { + + try { + ///////////////////////////////////////////// + /// LEXER AND PAXER (ANTLR) /// + //////////////////////////////////////////// + + let inputStream = new ANTLRInputStream(policyStr); + let lexer = new binding_grammarLexer(inputStream); + + let tokenStream = new CommonTokenStream(lexer); + let parser = new binding_grammarParser(tokenStream); + + parser.buildParseTree = true; + + let tree = parser.binding_policy(); + + let visitor = new BindingVisitor(); + + visitor.visit(tree); + + let policy = visitor.policy; + + ///////////////////////////////////////////// + /// CONSISTENCY AND SEMANTIC ANALYSIS /// + //////////////////////////////////////////// + + // (1) every role must appear as case creator or nominee in some statement. + for (let [key, value] of policy.roleIndexMap) { + let found = false; + if(policy.caseCreator !== key) { + for(let i = 0; i < policy.nominationStatements.length; i++) { + if(policy.nominationStatements[i].nominee == key) { + found = true; + break; + } + } + if(!found) + throw 'Role [' + key + '] cannot be nominated. \n Check that every role in the policy must appear as nominee, or case creator.'; + } + } + + // (2) In each statement the nominator and every role in the binding and endorsement constraints must be BOUND before the nominee + + // (2.1) Constructing the Nomination Net + // pB will be the index of the role (policy.indexRoleMap[role]), in case of self pU will be policy.indexRoleMap[role] + countRoles + + let transitions = []; + let marking = []; + marking[policy.roleIndexMap.get(policy.caseCreator)] = 1; + policy.nominationStatements.forEach(statement => { + let output = policy.roleIndexMap.get(statement.nominee); + let input = []; + let taken = []; + taken[policy.roleIndexMap.get(statement.nominee)] = 1; + let addArc = (rIndex) => { + if(taken[rIndex] !== 1) { + input.push(rIndex); + taken[rIndex] = 1; + } + } + let updateConstraint = (conjunctionSet) => { + conjunctionSet.forEach(andSet => { + andSet.roles.forEach(role => { + addArc(policy.roleIndexMap.get(role)); + }); + }) + } + if(statement.nominator === statement.nominee || statement.nominee === 'self') { + addArc(policy.roleIndexMap.get(statement.nominator) + policy.roleCount); + if(taken[policy.roleIndexMap.get(statement.nominator) + policy.roleCount] !== 1) + marking[policy.roleIndexMap.get(statement.nominator) + policy.roleCount] = 1; + } else + addArc(policy.roleIndexMap.get(statement.nominator)); + if(statement.bindingConstraint !== undefined) + updateConstraint(statement.bindingConstraint.conjunctionSets); + if(statement.endorsementConstraint !== undefined) + updateConstraint(statement.endorsementConstraint.conjunctionSets); + transitions.push({input: input, output: output}); + }); + + // (2.2) Validating the precedences (no dead transitions in the nomination net) + + let firedTransitions = []; + let firedCount = 0; + let roleOrder = []; + let level = 0; + + while(true) { + let toFire = []; + for(let i = 0; i < transitions.length; i++) { + if (firedTransitions[i] !== 1) { + let input = transitions[i].input; + // Validating If enabled + let enabled = true; + for(let j = 0; j < input.length; j++) + if(marking[input[j]] !== 1) { + enabled = false; + break; + } + if(enabled) + toFire.push(i); + } + } + // No new enabled transition + if(toFire.length === 0) + break; + level++; + // Firing new enabled transitions + toFire.forEach(trIndex => { + marking[transitions[trIndex].output] = 1; + firedTransitions[trIndex] = 1; + firedCount++; + roleOrder[transitions[trIndex].output] = level; + }) + // Every transition already fired, no dead transition + if(firedCount === transitions.length) + break; + } + if (firedCount < transitions.length) { + let invalid = ''; + for (let [key, value] of policy.roleIndexMap) { + if(marking[value] !== 1) + invalid += '[' + key + '] '; + } + throw 'Roles ' + invalid + 'cannot be nominated'; + } else { + console.log('Success, the policy is consistent. Role precedence:'); + console.log(0 + ': ' + policy.caseCreator); + for(let i = 1; i <= level; i++) { + let inLevel = ''; + for (let [key, value] of policy.roleIndexMap) { + if(roleOrder[value] === i) + inLevel += key + ' '; + } + console.log(i + ': ' + inLevel); + } + console.log('...............................................................'); + } + + ///////////////////////////////////////////// + /// SMART CONTRACT GENERATION /// + //////////////////////////////////////////// + + // (1) BitSet Operations + + let bitArrayToInteger = (bitarray) => { + if(bitarray.length > 0) { + let result = '0b'; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? '1' : '0'; + return new BigNumber(result).toFixed(); + } else { + return '0'; + } + } + + let roleMask = (roleId) => { + if(policy.roleIndexMap.has(roleId)) { + let bitarray = []; + bitarray[policy.roleIndexMap.get(roleId)] = 1; + return bitArrayToInteger(bitarray); + } else { + return '0'; + } + } + + let nominatorMask = (statementList, nominator) => { + let bitarray = []; + statementList.forEach(statement => { + if(statement.nominator === nominator) { + bitarray[policy.roleIndexMap.get(statement.nominee)] = 1; + } + }) + return bitArrayToInteger(bitarray); + } + + let disjunctionSetMask = (disjunctionSet) => { + let maskArray = []; + disjunctionSet.conjunctionSets.forEach(andSet => { + let bitarray = []; + andSet.roles.forEach(role => { + bitarray[policy.roleIndexMap.get(role)] = 1; + }); + maskArray.push(bitArrayToInteger(bitarray)); + }); + return maskArray; + } + + let disjunctionSetJoinMask = (disjunctionSet) => { + let bitarray = []; + disjunctionSet.conjunctionSets.forEach(andSet => { + andSet.roles.forEach(role => { + bitarray[policy.roleIndexMap.get(role)] = 1; + }); + }); + return bitArrayToInteger(bitarray); + } + + let statementMask = (statement) => { + let bitarray = []; + bitarray[policy.roleIndexMap.get(statement.nominator)] = 1; + bitarray[policy.roleIndexMap.get(statement.nominee)] = 1; + return bitArrayToInteger(bitarray); + } + + let endorsementRequiredMask = (statements) => { + let maskArray = []; + statements.forEach(statement => { + if (statement.endorsementConstraint !== undefined) + maskArray.push(statementMask(statement)); + }); + return maskArray; + } + + let codeGenerationInfo = { + policyName: policyName, + roleIndex: (roleId) => policy.roleIndexMap.get(roleId), + creatorMask: roleMask(policy.caseCreator), + statementMask: (statement) => statementMask(statement), + nominationStatements: policy.nominationStatements, + nominationMask: (nominator, statements) => nominatorMask(statements, nominator), + disjunctionSetJoinMask: (disjunctionSet) => disjunctionSetJoinMask(disjunctionSet), + disjunctionSetMask: (disjunctionSet) => disjunctionSetMask(disjunctionSet), + endorsementRequiredMask: (statements) => endorsementRequiredMask(statements), + releaseStatements: policy.releaseStatements + } + + policy.solidity = policy2solTemplate(codeGenerationInfo); + resolve(policy); + + } catch(ex) { + console.log('Error: ', ex); + reject(new Policy()); + } + + }) + +} + + + + + + + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyParser.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyParser.ts new file mode 100644 index 0000000..3ae92ce --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/BindingPolicyParser.ts @@ -0,0 +1,141 @@ + +import {binding_grammarVisitor} from '../antlr/binding_grammarVisitor'; + +import { ParseTree } from 'antlr4ts/tree/ParseTree'; +import { RuleNode } from 'antlr4ts/tree/RuleNode'; +import { TerminalNode } from 'antlr4ts/tree/TerminalNode'; +import { ErrorNode } from 'antlr4ts/tree/ErrorNode'; + +import { Override } from 'antlr4ts/Decorators'; + +import { + Policy, + Statement, + DisjunctionSet, + ConjunctionSet + } from './DataStructures'; + +import { + Binding_statementContext, + NominatorContext, + NomineeContext, + Is_creatorContext, + Binding_constrContext, + Set_expresionContext, + RoleContext, + Endorsement_constrContext, + Unbinding_statementContext +} from '../antlr/binding_grammarParser'; + + +export class BindingVisitor implements binding_grammarVisitor { + + _names: string[] = []; + policy: Policy = new Policy(); + + protected defaultResult(): string { + return ""; + } + + updateRules(names: string[]) { + this._names = names; + } + + @Override + visitTerminal(node: TerminalNode) { + let res = node.toStringTree(); + if(res === 'and') + return '&'; + else if(res === 'or') + return '|'; + else + return res; + } + + @Override + visitErrorNode(node: ErrorNode) { + let res = node.toStringTree(); + return res; + } + + visit(tree: ParseTree) { + return tree.accept(this); + } + visitUnbinding_statement(ctx: Unbinding_statementContext) : string { + let statement = new Statement(); + statement.nominator = ctx.nominator().accept(this); + statement.nominee = ctx.nominee().accept(this); + if(ctx.binding_constr() !== undefined) + statement.bindingConstraint = this.createDisjunctionSet(ctx.binding_constr().accept(this)); + if(ctx.endorsement_constr() !== undefined) + statement.endorsementConstraint = this.createDisjunctionSet(ctx.endorsement_constr().accept(this)); + this.policy.addReleaseStatement(statement); + return ''; + + } + visitBinding_statement(ctx: Binding_statementContext) : string { + if(ctx.is_creator() !== undefined) { + let creator = ctx.is_creator().accept(this); + this.policy.setCreator(creator); + } else { + let statement = new Statement(); + statement.nominator = ctx.nominator().accept(this); + statement.nominee = ctx.nominee().accept(this); + if(ctx.binding_constr() !== undefined) + statement.bindingConstraint = this.createDisjunctionSet(ctx.binding_constr().accept(this)); + if(ctx.endorsement_constr() !== undefined) + statement.endorsementConstraint = this.createDisjunctionSet(ctx.endorsement_constr().accept(this)); + this.policy.addNominationStatement(statement); + } + return ''; + } + visitIs_creator(ctx: Is_creatorContext) : string { + return ctx.role().text; + } + visitNominator (ctx: NominatorContext) : string { + this.policy.addRole(ctx.text); + return ctx.text; + } + visitNominee(ctx: NomineeContext) : string { + this.policy.addRole(ctx.text); + return ctx.text; + } + visitRole(ctx: RoleContext) : string { + this.policy.addRole(ctx.text); + return ctx.text; + } + visitBinding_constr(ctx: Binding_constrContext) : string { + let neg = ctx.NOT() === undefined ? '+' : '-'; + return neg + ctx.set_expresion().accept(this); + } + visitEndorsement_constr(ctx: Endorsement_constrContext) : string { + return '+' + ctx.set_expresion().accept(this); + } + visitSet_expresion(ctx: Set_expresionContext) : string { + let exp = ''; + for(let i = 0; i < ctx.childCount; i++) + exp += ctx.getChild(i).accept(this); + return exp; + } + visitChildren(node: RuleNode) : string { + + let nodeName = this._names[node.ruleContext.ruleIndex]; + let res = ''; + for (let i = 0; i < node.childCount; i++) + res += node.getChild(i).accept(this); + return res; + + } + createDisjunctionSet(setStr: string) : DisjunctionSet { + let disjunctionSet = new DisjunctionSet(); + disjunctionSet.isNegative = setStr[0] === '+' ? false : true; + let conjS = setStr.substr(1).split('|'); + conjS.forEach(value => { + let conjuntionSet = new ConjunctionSet(); + conjuntionSet.roles = value.split('&'); + disjunctionSet.conjunctionSets.push(conjuntionSet); + }) + return disjunctionSet; + } + +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/DataStructures.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/DataStructures.ts new file mode 100644 index 0000000..7ba5380 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/DataStructures.ts @@ -0,0 +1,81 @@ + +export class Policy { + caseCreator: string = undefined; + roleIndexMap: Map = new Map(); + roleCount: number = 0; + nominationStatements: Array = new Array(); + releaseStatements: Array = new Array(); + solidity: string = undefined; + + setCreator(caseCreator: string) { + this.caseCreator = caseCreator; + this.addRole(caseCreator); + } + addRole(roleId: string) : void { + if(!this.roleIndexMap.has(roleId)) + this.roleIndexMap.set(roleId, ++this.roleCount); + } + addNominationStatement(statement: Statement) : void { + this.nominationStatements.push(statement); + } + + addReleaseStatement(statement: Statement) : void { + this.releaseStatements.push(statement); + } + + print() { + console.log('Roles: ') + for (var [key, value] of this.roleIndexMap) { + console.log(key + ': ' + value); + } + console.log('---------------------------') + this.nominationStatements.forEach(value => { + value.print(); + console.log('---------------------------') + }) + } + +} +export class Statement { + nominator: string; + nominee: string; + bindingConstraint: DisjunctionSet = undefined; + endorsementConstraint: DisjunctionSet = undefined; + print() { + console.log('Nominator: ', this.nominator); + console.log('Nominee: ', this.nominee); + if(this.bindingConstraint !== undefined){ + console.log('Binding Constraints '); + this.bindingConstraint.print(); + } + if(this.endorsementConstraint !== undefined){ + console.log('Endorsement Constraints '); + this.endorsementConstraint.print(); + } + } + + } + export class DisjunctionSet { + isNegative: boolean; + conjunctionSets: Array = new Array(); + + + + + print() { + console.log(' Disjunction Set: ', this.isNegative ? 'NOT IN' : 'IN'); + this.conjunctionSets.forEach(value => { + value.print(); + + }) + } + } + export class ConjunctionSet { + roles: Array = new Array(); + + print() { + console.log(' [' + this.roles.toString() + ']') + } + } + + diff --git a/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/ProcessRoleGenerator.ts b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/ProcessRoleGenerator.ts new file mode 100644 index 0000000..17a8423 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/dynamic_binding/validation_code_gen/ProcessRoleGenerator.ts @@ -0,0 +1,59 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as ejs from "ejs"; + +const procesRole2solEJS = fs.readFileSync( + path.join(__dirname, "../../../../templates") + "/procesRole2sol.ejs", + "utf-8" +); + +const procesRole2ArrsolEJS = fs.readFileSync( + path.join(__dirname, "../../../../templates") + "/processRole2solArray.ejs", + "utf-8" +); + +let procesRole2solTemplate = ejs.compile(procesRole2solEJS); +let procesRole2solArrTemplate = ejs.compile(procesRole2ArrsolEJS); + +export let generateRoleTaskContract = (processData: Map>, contractName: string, isArray: boolean) => { + return new Promise((resolve, reject) => { + try { + ///////////////////////////////////////////// + /// SMART CONTRACT GENERATION /// + //////////////////////////////////////////// + + let sortedMaping: Map> = new Map(); + if(isArray) { + for (let [key, indexes] of processData) { + let maxTaskInd = 0; + indexes.forEach( index => { + maxTaskInd = index.taskIndex > maxTaskInd ? index.taskIndex : maxTaskInd; + }) + let sorted: Array = new Array(); + for(let i = 0; i <= maxTaskInd; i++) { + let toPush = 0; + indexes.forEach( index => { + if(index.taskIndex === i) + toPush = index.roleIndex; + }) + sorted.push(toPush); + } + sortedMaping.set(key, sorted); + } + } else { + sortedMaping = processData; + } + + let codeGenerationInfo = { + contractName: contractName, + processData: sortedMaping + } + let policySolidity = isArray ? procesRole2solArrTemplate(codeGenerationInfo) : procesRole2solTemplate(codeGenerationInfo); + resolve(policySolidity); + } catch(ex) { + console.log('Error: ', ex); + reject('Error on the contract creation'); + } + }) +} + diff --git a/v3.0/caterpillar-core/src/models/interpreter/BPMNParser.ts b/v3.0/caterpillar-core/src/models/interpreter/BPMNParser.ts new file mode 100644 index 0000000..461ab81 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/BPMNParser.ts @@ -0,0 +1,536 @@ +import * as BpmnModdle from "bpmn-moddle"; +import * as fs from "fs"; +import * as path from "path"; +import * as ejs from "ejs"; +import BigNumber from "bignumber.js"; + +import { SubProcessInfo, IFlowInfo, ElementIFlow, IDataInfo, ParamInfo, EdgeScript } from './ParsingInfo'; + + +const bpmn2solIDataEJS = fs.readFileSync( + path.join(__dirname, "../../../templates") + "/bpmn2solIData.ejs", + "utf-8" +); + +const factory2solEJS = fs.readFileSync( + path.join(__dirname, "../../../templates") + "/factory2Solidity.ejs", + "utf-8" +); + +let bpmn2solIDataTemplate = ejs.compile(bpmn2solIDataEJS); +let factory2solTemplate = ejs.compile(factory2solEJS); + +let moddle = new BpmnModdle(); +let parseBpmn = bpmnDoc => { + return new Promise((resolve, reject) => { + moddle.fromXML(bpmnDoc, (err, definitions) => { + if (!err) resolve(definitions); + else reject(err); + }); + }); +}; + +export let parseBpmnModel = (bpmnModel: string) => { + return new Promise((resolve, reject) => { + parseBpmn(bpmnModel) + .then((definitions: any) => { + + if (!definitions.diagrams || definitions.diagrams.length == 0) + throw new Error("ERROR: No diagram found in BPMN file"); + + let proc = definitions.rootElements[0]; + + let processInfo: SubProcessInfo = buildProcessTree(proc, 1, false); + + resolve(processInfo); + + // reject(new SubProcessInfo()); + + }); + }); + }; + + let buildProcessTree = (proc: any, instCount: number, eventSubProc: boolean) => { + + let processNode: SubProcessInfo = new SubProcessInfo(instCount); + processNode.procId = getNodeName(proc); + + let iFlow: IFlowInfo = new IFlowInfo(); + let iData: IDataInfo = new IDataInfo(); + + let arcCount = 1; + let elemCount = 1; + + let preCMap: Map> = new Map(); + let postCMap: Map> = new Map(); + let nextMap: Map> = new Map(); + let prevMap: Map> = new Map(); + let typeInfoMap: Map> = new Map(); + let isEvtGateway: Array = new Array(); + let eNames: Map = new Map(); + let isExternal: Array = new Array(); + + // Numbering the elements/arcs in the current Sub-process + proc.flowElements.forEach(element => { + switch(element.$type) { + case 'bpmn:SequenceFlow': { + iFlow.edgeIndexMap.set(element.id, arcCount++); + break; + } + default: { + if(element.$type === 'bpmn:UserTask' || element.$type === 'bpmn:ReceiveTask' || element.$type === 'bpmn:ServiceTask') + isExternal[elemCount] = true; + iFlow.nodeIndexMap.set(element.id, elemCount); + eNames.set(elemCount, getNodeName(element)); + preCMap.set(elemCount, new Array()); + postCMap.set(elemCount, new Array()); + nextMap.set(elemCount, new Array()); + prevMap.set(elemCount++, new Array()); + break; + } + } + }); + + if(proc.documentation) + updateGlobalFields(proc.documentation[0].text, iData); + + proc.flowElements.forEach(element => { + let eInd: number = iFlow.nodeIndexMap.get(element.id); + let typeInfo: Array = [] + let cardinality: number = 1; + + if(is(element.$type, 'task')) { + typeInfo[0] = 1; // Activity + typeInfo[3] = 1; // Task + } else if(is(element.$type, 'gateway' )) { + typeInfo[1] = 1; + } else if(is(element.$type, 'event' )) { + typeInfo[2] = 1; + iFlow.eventCodeMap.set(eInd, getNodeName(element)); + setEventType(typeInfo, element); + } + // Multi-Instances + if(element.loopCharacteristics) { + if(element.loopCharacteristics.isSequential) + typeInfo[7] = 1; // Sequential + else + typeInfo[6] = 1; // Parallel + if(element.loopCharacteristics.loopCardinality) + cardinality = parseInt(element.loopCharacteristics.loopCardinality.body); + } + switch(element.$type) { + case 'bpmn:SequenceFlow': { + let prevInd: number = iFlow.nodeIndexMap.get(element.sourceRef.id); + let nextInd: number = iFlow.nodeIndexMap.get(element.targetRef.id); + let arcInd: number = iFlow.edgeIndexMap.get(element.id); + postCMap.get(prevInd)[arcInd] = 1; + if(!(isExternal[nextInd] === true)) + nextMap.get(prevInd).push(nextInd); + preCMap.get(nextInd)[arcInd] = 1; + prevMap.get(nextInd).push(prevInd); + if(element.conditionExpression) { + if(!iData.gatewayScripts.has(prevInd)) + iData.gatewayScripts.set(prevInd, new Array()); + iData.gatewayScripts.get(prevInd).push(new EdgeScript(iFlow.edgeIndexMap.get(element.id), element.conditionExpression.body)); + } + break; + } + case 'bpmn:UserTask': { + typeInfo[11] = 1; + if(element.documentation) + updateParameters(element.documentation[0].text, eInd, iData); + checkEmptyParameters(eInd, iData); + break; + } + case 'bpmn:ScriptTask': { + typeInfo[12] = 1; + if(element.script) + iData.scripts.set(eInd, element.script); + break; + } + case 'bpmn:ServiceTask': { + typeInfo[13] = 1; + if(element.documentation) + updateParameters(element.documentation[0].text, eInd, iData); + checkEmptyParameters(eInd, iData); + break; + } + case 'bpmn:ReceiveTask': { + typeInfo[14] = 1; + checkEmptyParameters(eInd, iData); + break; + } + case 'bpmn:Task': { + typeInfo[10] = 1; + break; + } + case 'bpmn:ExclusiveGateway': { + typeInfo[4] = 1; + break; + } + case 'bpmn:ParallelGateway': { + typeInfo[5] = 1; + break; + } + case 'bpmn:EventBasedGateway': { + typeInfo[7] = 1; + isEvtGateway[eInd] = true; + break; + } + case 'bpmn:InclusiveGateway': { + typeInfo[6] = 1; + break; + } + case 'bpmn:CallActivity': { + typeInfo[0] = 1; + typeInfo[4] = 1; + break; + } + case 'bpmn:SubProcess': { + typeInfo[0] = 1; + typeInfo[5] = 1; + let evtSubP: boolean = false; + if(element.triggeredByEvent) { + // Event- SubProcess + typeInfo[12] = 1; + evtSubP = true; + } else { + // Expanded SubProcess + typeInfo[10] = 1; + } + let childProc: SubProcessInfo = buildProcessTree(element, cardinality, evtSubP); + childProc.parent = processNode; + processNode.children.push(childProc); + break; + } + case 'bpmn:StartEvent': { + if(eventSubProc) { + typeInfo[6] = 1; + if(!element.isInterrupting === false) + typeInfo[4] = 1; // Interrupting Event + } else + typeInfo[5] = 1; + setEventType(typeInfo, element); + break; + } + case 'bpmn:EndEvent': { + typeInfo[3] = 1; // Always Throw + typeInfo[9] = 1; + break; + } + case 'bpmn:IntermediateThrowEvent': { + typeInfo[3] = 1; + typeInfo[7] = 1; + break; + } + case 'bpmn:IntermediateCatchEvent': { + typeInfo[7] = 1; + break; + } + case 'bpmn:BoundaryEvent': { + typeInfo[8] = 1; + let subPInd: number = iFlow.nodeIndexMap.get(element.attachedToRef.id) + console.log(subPInd); + if(!iFlow.attachedEvents.has(subPInd)) + iFlow.attachedEvents.set(subPInd, new Array()); + iFlow.attachedEvents.get(subPInd).push(eInd); + if(!(element.cancelActivity === false)) { + typeInfo[4] = 1; + console.log(element) + } + break; + } + default: { + break; + } + } + typeInfoMap.set(eInd, typeInfo); + }); + + proc.flowElements.forEach(element => { + if(is(element.$type, 'gateway' )) { + let eInd: number = iFlow.nodeIndexMap.get(element.id); + if(prevMap.get(eInd).length > 1) + typeInfoMap.get(eInd)[3] = 1; + } + }); + + iFlow.nodeIndexMap.forEach((eInd, eId) => { + let elementInfo: ElementIFlow = new ElementIFlow(eInd, eNames.get(eInd)); + elementInfo.typeInfo = toStrDecimal(typeInfoMap.get(eInd)); + elementInfo.postC = toStrDecimal(postCMap.get(eInd)); + elementInfo.next = nextMap.get(eInd); + if(prevMap.get(eInd).length > 0 && isEvtGateway[prevMap.get(eInd)[0]]) + elementInfo.preC = toStrDecimal(postCMap.get(prevMap.get(eInd)[0])) + else + elementInfo.preC = toStrDecimal(preCMap.get(eInd)); + iFlow.elementInfo.set(eInd, elementInfo); + }); + + let codeGenerationInfo = { + contractName: getNodeName(proc), + globalFields: iData.globalFields, + elementBitMask: eInd => { + let bitarray = []; + bitarray[eInd] = 1; + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + indexArrayToBitSet: (indexArray: Array) => { return arrToStrDecimal(indexArray); }, + gatewayScript: iData.gatewayScripts, + taskScripts: iData.scripts, + postC: (eInd: number) => { return iFlow.elementInfo.get(eInd).postC }, + checkOutList: groupOutParam(iData.outParams), + checkOutReturn: (params: Array, returnType: string) => { + let res = ''; + for(let i = 0; i < params.length - 1; i++) + res += ' ' + params[i] + ',' + if(params.length > 0) + res = returnType + '(' + res + params[params.length - 1] + ')'; + return res; + }, + checkInList: groupInParam(iData.inParams, iData.userScripts), + checkInParams: (params: Array, renamedParams: Array) => { + let res = ''; + for(let i = 0; i < params.length; i++) + res += ', ' + params[i].type + ' ' + renamedParams[i]; + return res; + } + } + iData.iDataSolidity = bpmn2solIDataTemplate(codeGenerationInfo); + iData.factorySolidity = factory2solTemplate(codeGenerationInfo); + + processNode.iData = iData; + processNode.iflow = iFlow; + + return processNode; + } + + let groupOutParam = (candidates: Map>) => { + + let resMap: Map = new Map(); + let resArray: Array = new Array(); + + candidates.forEach((params: Array, eInd: number) => { + let joinKey: string = 'e'; + let types: Array = new Array(); + let names: Array = new Array(); + let funcName: string = 'checkOut'; + + params.forEach(param => { + funcName += param.type.charAt(0).toLocaleUpperCase() + param.type.charAt(1); + joinKey += param.type; + types.push(param.type); + names.push(param.name); + }); + if(!resMap.has(joinKey)) { + resMap.set(joinKey, resArray.length); + resArray.push( + { + funcName: funcName, + types: types, + eIndexes: new Array(), + paramNames: new Array() + } + ) + } + let arrInd: number = resMap.get(joinKey); + resArray[arrInd].eIndexes.push(eInd); + resArray[arrInd].paramNames.push(names); + }); + return resArray; + }; + + let groupInParam = (candidates: Map>, scripts: Map) => { + let resMap: Map = new Map(); + let resArray: Array = new Array(); + + candidates.forEach((params: Array, eInd: number) => { + + let joinKey: string = 'e'; + let renamedParams: Array = new Array(); + let renamedScript: string = scripts.get(eInd); + + let i: number = 1; + + params.forEach(param => { + joinKey += param.type; + renamedParams.push('i' + i); + if(renamedScript) + renamedScript = renamedScript.replace(param.name, 'i' + i++); + }); + + if(!resMap.has(joinKey)) { + resMap.set(joinKey, resArray.length); + resArray.push( + { + params: params, + renamedParams: renamedParams, + eIndexes: new Array(), + scripts: new Array() + } + ) + } + let arrInd: number = resMap.get(joinKey); + resArray[arrInd].eIndexes.push(eInd); + resArray[arrInd].scripts.push(renamedScript); + }) + return resArray; + } + + let sortParameters = (params: Array) => { + params.sort(compareTo); + } + + let compareTo = (a:ParamInfo, b:ParamInfo) => { + if (a.type < b.type) return -1; + if (a.type > b.type) return 1; + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + } + + let toStrDecimal = (bitArray => { + let result = '0b'; + for (let i = bitArray.length - 1; i >= 0; i--) + result += bitArray[i] ? '1' : '0'; + return result === '0b' ? '0' : new BigNumber(result).toFixed(); + }) + + let arrToStrDecimal = ((indexArray: Array) => { + let bitarray: Array = new Array(); + for(let i = 0; i < indexArray.length; i++) + bitarray[indexArray[i]] = 1; + return toStrDecimal(bitarray); + }); + + let is = (element: string, type: string) => { + return element.toLocaleLowerCase().includes(type.toLocaleLowerCase()); + } + + let setEventType = (typeInfo: Array, element: any) => { + if(element.eventDefinitions) { + switch(element.eventDefinitions[0].$type) { + case 'bpmn:ErrorEventDefinition': { + typeInfo[13] = 1; + break; + } + case 'bpmn:EscalationEventDefinition': { + typeInfo[14] = 1; + break; + } + case 'bpmn:MessageEventDefinition': { + typeInfo[12] = 1; + break; + } + case 'bpmn:SignalEventDefinition': { + typeInfo[15] = 1; + break; + } + case 'bpmn:TerminateEventDefinition': { + typeInfo[11] = 1; + break; + } + } + } else { + // Default (None) event + typeInfo[10] = 1; + } + + } + + let getNodeName = (node: any) => node.name ? node.name.replace(/\s+/g, "_") : node.id; + + let updateGlobalFields = (cad: string, iData: IDataInfo) => { + if(!cad) + return; + let arr = cad.split(';'); + arr.forEach( gField => { + if(gField.trim().length > 0) { + let arr1 = gField.trim().split(' '); + iData.globalFields.push(new ParamInfo(arr1[0], arr1[arr1.length -1])); + } + }) + } + + let checkEmptyParameters = (eInd: number, iData: IDataInfo) => { + if(!iData.inParams.has(eInd)) + iData.inParams.set(eInd, new Array()); + if(!iData.outParams.has(eInd)) + iData.outParams.set(eInd, new Array()); + } + + let updateParameters = (cad: string, eInd: number, iData: IDataInfo) => { + let arr = cad.split('@'); + if(arr.length >= 3) { + // if(controlFlowInfo != null) + // controlFlowInfo.taskRoleMap.set(nodeId, arr[1].trim()); + if(arr[2].length > 1) + cad = arr[2]; + else + return; + // else + // return undefined; + } + // Processing Information of function parameters (both service and user tasks) + cad = cad + .replace("(", " ") + .replace(")", " ") + .trim(); + cad = cad + .replace("(", " ") + .replace(")", " ") + .trim(); + + let firstSplit = cad.split(":"); + if (firstSplit.length > 2) { + let aux = ''; + for (let i = 1; i < firstSplit.length; i++) + aux += firstSplit[i]; + firstSplit = [firstSplit[0], aux]; + } + + let secondSplit = firstSplit[firstSplit.length - 1].trim().split("->"); + let resMap: Map> = new Map(); + let inputOutput = [firstSplit[0].trim(), secondSplit[0].trim()]; + let parameterType = ["input", "output"]; + resMap.set("body", [secondSplit[secondSplit.length - 1].trim()]); + + for (let i = 0; i < inputOutput.length; i++) { + let temp = inputOutput[i].split(","); + let res = []; + temp.forEach(subCad => { + let aux = subCad.trim().split(" "); + if (aux[0].trim().length > 0) { + res.push(aux[0].trim()); + res.push(aux[aux.length - 1].trim()); + } + }); + resMap.set(parameterType[i], res); + } + + let inParameters: Array = []; + let outParameters: Array = []; + + let toIterate = resMap.get('output'); + for (let i = 0; i < toIterate.length; i += 2) + inParameters.push(new ParamInfo(toIterate[i], toIterate[i + 1])); + + sortParameters(inParameters); + iData.inParams.set(eInd, inParameters); + iData.userScripts.set(eInd, resMap.get("body")[0]); + toIterate = resMap.get('input'); + + for (let i = 0; i < toIterate.length; i += 2) + outParameters.push(new ParamInfo(toIterate[i], toIterate[i + 1])); + + sortParameters(outParameters); + iData.outParams.set(eInd, outParameters) + }; + + + + + diff --git a/v3.0/caterpillar-core/src/models/interpreter/ParsingInfo.ts b/v3.0/caterpillar-core/src/models/interpreter/ParsingInfo.ts new file mode 100644 index 0000000..d5ef894 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/ParsingInfo.ts @@ -0,0 +1,56 @@ + +export class SubProcessInfo { + procId: string; + + iflow: IFlowInfo; + + iData: IDataInfo; + + parent: SubProcessInfo; + + children: Array = new Array(); + + constructor(public instCount: number) {} +} + +export class IFlowInfo { + nodeIndexMap: Map = new Map(); + edgeIndexMap: Map = new Map(); + elementInfo: Map = new Map(); + attachedEvents: Map> = new Map(); + eventCodeMap: Map = new Map(); +} + +export class ElementIFlow { + preC: string; + postC: string; + typeInfo: string; + next: Array; + constructor(public eInd: number, public eName: string){} +} + +export class IDataInfo { + globalFields: Array = new Array(); + + scripts: Map = new Map(); + + userScripts: Map = new Map(); + + gatewayScripts: Map> = new Map(); + + inParams: Map> = new Map(); + + outParams: Map> = new Map(); + + iDataSolidity: string; + + factorySolidity: string +} + +export class EdgeScript { + constructor(public edgeInd: number, public script: string) {} +} + +export class ParamInfo { + constructor(public type: string, public name: string) {} +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/BPMNInterpreter.sol b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/BPMNInterpreter.sol new file mode 100644 index 0000000..bf0d059 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/BPMNInterpreter.sol @@ -0,0 +1,347 @@ +pragma solidity ^0.4.25; + +import 'IData'; +import 'IFlow'; +import 'IFactory'; + +contract BPMNInterpreter { + + event MessageSent(bytes32 messageBody); + event NewCaseCreated(address pCase); + + /// Instantiation of Root-Process + function createInstance(address cFlow) public returns(address) { + address factory = IFlow(cFlow).getFactoryInst(); + require(factory != address(0)); + + address pCase = IFactory(factory).newInstance(); + IData(pCase).setParent(address(0), cFlow, 0); + + emit NewCaseCreated(pCase); + + executionRequired(cFlow, pCase); + + } + + /// Instantiation of a sub-process by its parent + function createInstance(uint eInd, address pCase) private returns(address) { + address pFlow = IData(pCase).getCFlowInst(); + address cFlow = IFlow(pFlow).getSubProcInst(eInd); + + address factory = IFlow(cFlow).getFactoryInst(); + require(factory != address(0)); + + address cCase = IFactory(factory).newInstance(); + + IData(cCase).setParent(pCase, cFlow, eInd); + IData(pCase).addChild(eInd, cCase); + + executionRequired(cFlow, cCase); + + return cCase; + } + + function executionRequired(address cFlow, address pCase) private { + uint eFirst = IFlow(cFlow).getFirstElement(); + IData(pCase).setMarking(IFlow(cFlow).getPostCond(eFirst)); + + uint[] memory next = IFlow(cFlow).getAdyElements(eFirst); + + if(next.length > 0) + executeElements(pCase, next[0]); + } + + function throwEvent(address pCase, bytes32 evCode, uint evInfo) private { + + /// This function only receive THROW EVENTS (throw event verification made in function executeElement) + + uint[2] memory pState; + pState[0] = IData(pCase).getMarking(); + pState[1] = IData(pCase).getStartedActivities(); + + if(evInfo & 4096 == 4096) + /// Message (BIT 15), to publish a Message in the Ethereum Event Log + emit MessageSent(evCode); + if(evInfo & 5632 == 5632) { + /// 9- End, 10- Default, 12- Message + if(pState[0] | pState[1] == 0) // If there are not tokens to consume nor started activities in any subprocess + tryCatchEvent(pCase, evCode, evInfo, true); // Sub-process ended, thus continue execution on parent + } else { + if(evInfo & 2048 == 2048) + /// Terminate Event (BIT 11), only END EVENT from standard, + killProcess(pCase); // Terminate the execution in the current Sub-process and each children + tryCatchEvent(pCase, evCode, evInfo, pState[0] | pState[1] == 0); // Continue the execution on parent + } + } + + + function tryCatchEvent(address pCase, bytes32 evCode, uint evInfo, bool isInstCompl) private { + + address catchCase = IData(pCase).getParent(); + if(catchCase == address(0)) { + /// No Parent exist, root node + if(evInfo & 8192 == 8192) + /// Error event (BIT 13), only END EVENT from standard, in the root process. + killProcess(pCase); + return; + } + + address cFlow = IData(catchCase).getCFlowInst(); + + uint[2] memory pState; + pState[0] = IData(catchCase).getMarking(); + pState[1] = IData(catchCase).getStartedActivities(); + + uint subProcInd = IData(pCase).getIndexInParent(); + + uint runInstCount = isInstCompl ? IData(catchCase).decreaseInstanceCount(subProcInd) + : IData(catchCase).getInstanceCount(subProcInd); + + if(runInstCount == 0) + /// Update the corresponding sub-process, call activity as completed + IData(catchCase).setActivityMarking(pState[1] & ~(1 << 1 << subProcInd)); + + uint subProcInfo = IFlow(cFlow).getTypeInfo(subProcInd); + + if(evInfo & 7168 != 0) { + /// If receiving 10- Default, 11- Terminate or 12- Message + if(runInstCount == 0 && subProcInfo & 4096 != 4096) { + /// No Instances of the sub-process propagating the event and The sub-process isn't an event-sub-process (BIT 12) + IData(catchCase).setMarking(pState[0] & ~IFlow(cFlow).getPostCond(subProcInd)); + executeElements(catchCase, IFlow(cFlow).getAdyElements(subProcInfo)[0]); + } else if(subProcInfo & 128 == 128) { + /// Multi-Instance Sequential (BIT 7), with pending instances to be started. + createInstance(subProcInd, pCase); + } + } else { + /// Signal, Error or Escalation + + /// Signals are only handled from the Root-Process by Broadcast, thus the propagation must reach the Root-Process. + if(evInfo & 32768 == 32768) { + /// Propagating the Signal to the Root-Process + while(catchCase != address(0)) { + pCase = catchCase; + catchCase = IData(pCase).getParent(); + } + broadcastSignal(pCase); + return; + } + + uint[] memory events = IFlow(cFlow).getEventList(); + + /// The event can be catched only once, unless it is a signal where a broadcast must happen. + /// Precondition: Event-subprocess must appear before boundary events on the event list. + for(uint i = 0; i < events.length; i++) { + + if(IFlow(cFlow).getEventCode(events[i]) == evCode) { + /// Verifiying there is a match with the throw-cath events. + + uint catchEvInfo = IFlow(cFlow).getTypeInfo(events[i]); // Info of the candidate catching event + uint attachedTo = IFlow(cFlow).getAttachedTo(events[i]); // + + if(catchEvInfo & 6 == 6) { + /// Start event-sub-process (BIT 6) + if(catchEvInfo & 16 == 16) + /// Interrupting (BIT 4 must be 1, 0 if non-interrupting) + killProcess(catchCase); // Before starting the event subprocess, the parent is killed + + createInstance(attachedTo, pCase); // Starting event sub-process + IData(catchCase).setActivityMarking(pState[1] | (1 << attachedTo)); // Marking the event-sub-process as started + return; + } else if(catchEvInfo & 256 == 256 && attachedTo == subProcInd) { + /// Boundary (BIT 6) of the subproces propagating the event + if(catchEvInfo & 16 == 16) + /// Interrupting (BIT 4 must be 1, 0 if non-interrupting) + killProcess(pCase); // The subprocess propagating the event must be interrupted + + IData(catchCase).setMarking(pState[0] & ~IFlow(cFlow).getPostCond(events[i])); // Update the marking with the output of the boundary event + executeElements(catchCase, IFlow(cFlow).getAdyElements(events[i])[0]); // Continue the execution of possible internal elements + return; + } + } + } + /// If the event was not caught the propagation continues to the parent unless it's the root process + throwEvent(catchCase, evCode, evInfo); + } + } + + function killProcess(address pCase) private { + uint startedActivities = IData(pCase).getStartedActivities(); + IData(pCase).setMarking(0); + IData(pCase).setActivityMarking(0); + + uint[] memory children = IFlow(IData(pCase).getCFlowInst()).getSubProcList(); + + for(uint i = 0; i < children.length; i++) + if(startedActivities & (1 << children[i]) != 0) + killProcess(IData(pCase).getChildProcInst(children[i])); + } + + function killProcess(address[] memory pCases) private { + for(uint i = 0; i < pCases.length; i++) + killProcess(pCases[i]); + } + + + function broadcastSignal(address pCase) private { + + address cFlow = IData(pCase).getCFlowInst(); + + uint[] memory events = IFlow(cFlow).getEventList(); + + uint[2] memory pState; + pState[1] = IData(pCase).getStartedActivities(); + + for(uint i = 0; i < events.length; i++) { + uint evInfo = IFlow(cFlow).getTypeInfo(events[i]); + + if(evInfo & 32780 == 32772) { + /// Event Catch Signal (BITs 2, 3 [0-catch, 1-throw], 15) + + uint catchEvInfo = IFlow(cFlow).getTypeInfo(events[i]); + uint attachedTo = IFlow(cFlow).getAttachedTo(events[i]); + + if(catchEvInfo & 6 == 6) { + /// Start event-sub-process (BIT 6) + if(catchEvInfo & 16 == 16) + /// Interrupting (BIT 4 must be 1, 0 if non-interrupting) + killProcess(pCase); // Before starting the event subprocess, the current process-instance is killed + + createInstance(attachedTo, pCase); // Starting event sub-process + IData(pCase).setActivityMarking(1 << attachedTo); // Marking the event-sub-process as started + } else if(catchEvInfo & 256 == 256) { + /// Boundary (BIT 6) of the subproces propagating the event + if(catchEvInfo & 16 == 16) + /// Interrupting (BIT 4 must be 1, 0 if non-interrupting) + killProcess(IData(pCase).getChildProcInst(attachedTo)); // The subprocess propagating the event must be interrupted + + IData(pCase).setMarking(IData(pCase).getMarking() & ~IFlow(cFlow).getPostCond(events[i])); // Update the marking with the output of the boundary event + executeElements(pCase, IFlow(cFlow).getAdyElements(events[i])[0]); // Continue the execution of possible internal elements + } else if(evInfo & 160 == 160) { + /// Start (not Event Subprocess) OR Intermediate Event + IData(pCase).setMarking(IData(pCase).getMarking() & ~IFlow(cFlow).getPreCond(events[i]) | IFlow(cFlow).getPostCond(events[i])); + executeElements(pCase, IFlow(cFlow).getAdyElements(events[i])[0]); + } + } + } + + uint[] memory children = IFlow(IData(pCase).getCFlowInst()).getSubProcList(); + uint startedActivities = IData(pCase).getStartedActivities(); + + for(uint j = 0; j < children.length; j++) + if(startedActivities & (1 << children[j]) != 0) + broadcastSignal(IData(pCase).getChildProcInst(children[j])); + } + + function broadcastSignal(address[] memory pCases) private { + for(uint i = 0; i < pCases.length; i++) + broadcastSignal(pCases[i]); + } + + + function executeElements(address pCase, uint eInd) public { + address cFlow = IData(pCase).getCFlowInst(); + uint[] memory next; + + /// Declared as an array and not as independent fields to avoid Stack Too Deep- Compilation Error + + /// 0- preC + /// 1- postC + /// 2- typeInfo + uint[3] memory lInfo; + + /// 0- tokensOnEdges + /// 1- startedActivities + uint[2] memory pState; + + pState[0] = IData(pCase).getMarking(); + pState[1] = IData(pCase).getStartedActivities(); + + /// Execution queue and pointers to the first & last element (i.e. basic circular queue implementation) + uint[100] memory queue; + uint i = 0; uint count = 0; + + queue[count++] = eInd; + + while (i < count) { + + eInd = queue[i++]; + + (lInfo[0], lInfo[1], lInfo[2], next) = IFlow(cFlow).getElementInfo(eInd); + // (preC, postC, typeInfo, next) = IFlow(cFlow).getElementInfo(eInd); + + /// Verifying Preconditions (i.e. Is the element enabled?) + + + if(lInfo[2] & 42 == 42 ) { + /// else if (AND Join) + if(pState[0] & lInfo[0] != lInfo[0]) + continue; + pState[0] &= ~lInfo[0]; + } else if(lInfo[2] & 74 == 74 ) { + /// else if (OR Join) + ///// OR Join Implementation ////// + } else if(lInfo[2] & 1 == 1 || (lInfo[2] & 4 == 4 && lInfo[2] & 640 != 0) || lInfo[2] & 2 == 2) { + /// If (Activity || Intermediate/End Event || Gateway != AND/OR Join) + if(pState[0] & lInfo[0] == 0) + continue; + pState[0] &= ~lInfo[0]; // Removing tokens from input arcs + } else { + continue; + } + + /// Executing current element (If enabled) + + if(lInfo[2] & 65 == 65) { + /// (0- Activity, 6- Parallel Multi-Instance) + uint cInst = IFlow(cFlow).getInstanceCount(eInd); + while(cInst-- > 0) + createInstance(eInd, pCase); + pState[1] |= (1 << eInd); + } else if(lInfo[2] & 129 == 129 || (lInfo[2] & 1 == 1 && lInfo[2] & 48 != 0 && lInfo[2] & 4096 == 0)) { + /// If (0- Activity, 7- Sequential Multi-Instance) || + /// Sub-process(0- Activity, 5- Sub-process) or Call-Activity(0- Activity, 4- Call-Activity) + /// but NOT Event Sub-process(12- Event Subprocess) + IData(createInstance(eInd, pCase)).setInstanceCount(eInd, IFlow(cFlow).getInstanceCount(eInd)); + pState[1] |= (1 << eInd); + } else if(lInfo[2] & 4105 == 4105 || (lInfo[2] & 10 == 2 && lInfo[2] & 80 != 0)) { + /// (0- Activity, 3- Task, 12- Script) || + /// Exclusive(XOR) Split (1- Gateway, 3- Split(0), 4- Exclusive) || + /// Inclusive(OR) Split (1- Gateway, 3- Split(0), 6- Inclusive) + IData(pCase).executeScript(eInd); + pState[0] |= IData(pCase).executeScript(eInd); + } else if ((lInfo[2] & 9 == 9 && lInfo[2] & 27657 != 0) || lInfo[2] & 2 == 2) { + /// If (User(11), Service(13), Receive(14) or Default(10) Task || Gateways(1) not XOR/OR Split) + /// The execution of User/Service/Receive is triggered off-chain, + /// Thus the starting point would be the data contract which executes any script/data-update related to the task. + pState[0] |= lInfo[1]; + } else if(lInfo[2] & 12 == 12) { + /// If (2- Event, 3- Throw(1)) + IData(pCase).setMarking(pState[0]); + IData(pCase).setActivityMarking(pState[1]); + throwEvent(pCase, IFlow(cFlow).getEventCode(eInd), lInfo[2]); + if(IData(pCase).getMarking() | IData(pCase).getStartedActivities() == 0) + /// By throwing the event, a kill was performed so the current instance was terminated + return; + pState[0] = IData(pCase).getMarking(); + pState[1] = IData(pCase).getStartedActivities(); + + if(lInfo[2] & 128 == 128) + /// If Intermediate event (BIT 7) + pState[0] |= lInfo[1]; + } + + /// Adding the possible candidates to be executed to the queue. + /// The enablement of the element is checked at the moment it gets out of the queue. + for(uint j = 0; j < next.length; j++) { + queue[count] = next[j]; + count = ++count % 100; + } + } + + /// Updating the state (storage) after the execution of each internal element. + IData(pCase).setMarking(pState[0]); + IData(pCase).setActivityMarking(pState[1]); + + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IData.sol b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IData.sol new file mode 100644 index 0000000..0c162ca --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IData.sol @@ -0,0 +1,100 @@ +pragma solidity ^0.4.25; + +contract IData { + + function setActivityMarking(uint nMarking) public; + function setMarking(uint nMarking) public; + function setParent(address parent, address cFlow, uint eInd) public; + function addChild(uint eInd, address child) public; + function getMarking() public view returns(uint); + function getStartedActivities() public view returns(uint); + function getInstanceCount(uint eInd) public view returns(uint); + function decreaseInstanceCount(uint eInd) public returns(uint); + function setInstanceCount(uint eInd, uint instC) public; + function getIndexInParent() public view returns(uint); + function executeScript(uint eInd) public returns(uint); + function getChildProcInst(uint eInd) public view returns(address[] memory); + function getCFlowInst() public view returns(address); + function getParent() public view returns(address); + function continueExecution(uint eInd) public; +} + +contract IInterpreter { + function getInterpreterInst() public view returns(address); + function executeElements(address pCase, uint eInd) public; +} + +contract IDataImp { + + uint private tokensOnEdges; + uint private startedActivities; + + address private iDataParent; + address private iFlowNode; + uint private indexInParent; + + mapping(uint => address[]) private children; + mapping(uint => uint) private instCount; + + function setActivityMarking(uint nMarking) public { + startedActivities = nMarking; + } + + function setMarking(uint nMarking) public { + tokensOnEdges = nMarking; + } + + function setParent(address parent, address cFlow, uint eInd) public { + indexInParent = eInd; + iDataParent = parent; + iFlowNode = cFlow; + } + + function addChild(uint eInd, address child) public { + children[eInd].push(child); + instCount[eInd]++; + } + + function getMarking() public view returns(uint) { + return tokensOnEdges; + } + + function getStartedActivities() public view returns(uint) { + return startedActivities; + } + + function getInstanceCount(uint eInd) public view returns(uint) { + return instCount[eInd]; + } + + function decreaseInstanceCount(uint eInd) public returns(uint) { + instCount[eInd]--; + } + + function setInstanceCount(uint eInd, uint instC) public { + instCount[eInd] = instC; + } + + function getIndexInParent() public view returns(uint) { + return indexInParent; + } + + function getChildProcInst(uint eInd) public view returns(address[] memory) { + return children[eInd]; + } + + function getCFlowInst() public view returns(address) { + return iFlowNode; + } + + function getParent() public view returns(address) { + return iDataParent; + } + + function continueExecution(uint eInd) public { + address interpreter = IInterpreter(iFlowNode).getInterpreterInst(); + IInterpreter(interpreter).executeElements(address(this), eInd); + } + +} + diff --git a/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFactory.sol b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFactory.sol new file mode 100644 index 0000000..8fd3cd6 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFactory.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.25; + +contract IFactory { + function newInstance() public returns(address); +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFlow.sol b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFlow.sol new file mode 100644 index 0000000..9ae1fe4 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/interpreter/smart_contracts/IFlow.sol @@ -0,0 +1,153 @@ +pragma solidity ^0.4.25; + +contract IFlow { + + function getPreCond(uint eInd) public view returns(uint); + function getPostCond(uint eInd) public view returns(uint); + function getTypeInfo(uint eInd) public view returns(uint); + function getAdyElements(uint eInd) public view returns(uint[] memory); + function getEventCode(uint eInd) public view returns(bytes32); + function getAttachedTo(uint eInd) public view returns(uint); + + function getElementInfo(uint eInd) public view returns(uint, uint, uint, uint[] memory); + function getFirstElement() public view returns(uint); + + function getEventList() public view returns(uint[] memory); + function getSubProcList() public view returns(uint[] memory); + function getSubProcInst(uint eInd) public view returns(address); + function getFactoryInst() public view returns(address); + function setFactoryInst(address _factory) public; + function getInstanceCount(uint eInd) public view returns(uint); + + function getInterpreterInst() public view returns(address); + function setInterpreterInst(address _interpreter) public; + + function setElement(uint eInd, uint preC, uint postC, uint typeInfo, bytes32 eCode, uint[] memory _nextElem) public; + function linkSubProcess(uint pInd, address cFlowInst, uint[] memory attachedEvt, uint countInstances) public; + +} + +/// @title Control Flow Data Structure +/// @dev Tree mirroring the process hierarchy derived from a process model. +/// @dev This contract must be deployed/instantiated once per process model. +contract IFlowImpl { + + uint private startEvt; + address private factory; + address private interpreter; + + // elemIndex => [preC, postC, type] + mapping(uint => uint[3]) private condTable; + + // Element Index => List of elements that can be enabled with the completion of the key element + mapping(uint => uint[]) private nextElem; + + // List of Indexes of the subprocesses + uint[] private subProcesses; + + // List of Event Indexes defined in the current Subprocess + uint[] private events; + + // Event Index => Index of the element where event is attachedTo + mapping(uint => uint) private attachedTo; + + // Event Index => String representing the code to identify the event (for catching) + mapping(uint => bytes32) private eventCode; + + // Subprocess Index => Child Subproces address + mapping(uint => address) private pReferences; + + // Subprocess Index => number of instances + mapping(uint => uint) private instanceCount; + + + function getPreCond(uint eInd) public view returns(uint) { + return condTable[eInd][0]; + } + + function getPostCond(uint eInd) public view returns(uint) { + return condTable[eInd][1]; + } + + function getTypeInfo(uint eInd) public view returns(uint) { + return condTable[eInd][2]; + } + + function getFirstElement() public view returns(uint) { + return startEvt; + } + + function getAdyElements(uint eInd) public view returns(uint[] memory) { + return nextElem[eInd]; + } + + function getElementInfo(uint eInd) public view returns(uint, uint, uint, uint[] memory) { + return (condTable[eInd][0], condTable[eInd][1], condTable[eInd][2], nextElem[eInd]); + } + + function getSubProcList() public view returns(uint[] memory) { + return subProcesses; + } + + function getInstanceCount(uint eInd) public view returns(uint) { + return instanceCount[eInd]; + } + + function getEventCode(uint eInd) public view returns(bytes32) { + return eventCode[eInd]; + } + + function getEventList() public view returns(uint[] memory) { + return events; + } + + function getAttachedTo(uint eInd) public view returns(uint) { + return attachedTo[eInd]; + } + + function getSubProcInst(uint eInd) public view returns(address) { + return pReferences[eInd]; + } + + function getFactoryInst() public view returns(address) { + return factory; + } + + function setFactoryInst(address _factory) public { + factory = _factory; + } + + function getInterpreterInst() public view returns(address) { + return interpreter; + } + + function setInterpreterInst(address _interpreter) public { + interpreter = _interpreter; + } + + function setElement(uint eInd, uint preC, uint postC, uint typeInfo, bytes32 eCode, uint[] memory _nextElem) public { + uint _typeInfo = condTable[eInd][2]; + if (_typeInfo == 0) { + if (typeInfo & 4 == 4) { + events.push(eInd); + if (typeInfo & 36 == 36) + startEvt = eInd; + eventCode[eInd] = eCode; + } else if (typeInfo & 33 == 33) + subProcesses.push(eInd); + } else + require (_typeInfo == typeInfo); + condTable[eInd] = [preC, postC, typeInfo]; + nextElem[eInd] = _nextElem; + + } + + function linkSubProcess(uint pInd, address cFlowInst, uint[] memory attachedEvt, uint countInstances) public { + require(condTable[pInd][2] & 33 == 33); // BITs (0, 5) Veryfing the subprocess to link is already in the data structure + pReferences[pInd] = cFlowInst; + for(uint i = 0; i < attachedEvt.length; i++) + if(condTable[attachedEvt[i]][2] & 4 == 4) + attachedTo[attachedEvt[i]] = pInd; + instanceCount[pInd] = countInstances; + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/models/models.controller.ts b/v3.0/caterpillar-core/src/models/models.controller.ts new file mode 100644 index 0000000..5eacc4a --- /dev/null +++ b/v3.0/caterpillar-core/src/models/models.controller.ts @@ -0,0 +1,1887 @@ + +import {Router} from 'express'; +import * as solc from 'solc'; + +import {AbiCoder} from 'web3-eth-abi'; +const abiCoder = new AbiCoder(); + +import * as Web3 from 'web3'; +import BigNumber from "bignumber.js"; + +import {ModelInfo} from './definitions'; +import {parseModel} from './models.parsers'; +import {repoSchema} from '../repo/procModelData'; +import {registrySchema} from '../repo/procModelData'; +import {policySchema} from '../repo/procModelData'; +import {roleTaskSchema} from '../repo/procModelData'; + +import {generatePolicy} from './dynamic_binding/validation_code_gen/BindingPolicyGenerator'; +import {generateRoleTaskContract} from './dynamic_binding/validation_code_gen/ProcessRoleGenerator'; +import {SubProcessInfo, IFlowInfo, IDataInfo, ElementIFlow, ParamInfo} from './interpreter/ParsingInfo'; + + +import {parseBpmnModel} from './interpreter/BPMNParser'; + +// import * as mongoose from 'mongoose'; + +// let app = require('express')(); +// let http = require('http').Server(app); + +// let io = require('socket.io')(http); +// let ObjectId = mongoose.Types.ObjectId; + +/* http.listen(8090, () => { + // console.log('started on port 8090'); +}); */ + +import fs = require('fs'); + + +////// ANTLR Runtime Requirements ////////////////////////// + + + +//////////////////////////////////////////////////////////// + +const models: Router = Router(); +let web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); +// var web3 = new Web3(new Web3.providers.HttpProvider("http://193.40.11.64:80")); + +const WebSocket = require('ws'); +let mws; +const wss = new WebSocket.Server({port: 8090}); +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + // console.log('received: %s', message); + }); + ws.on('error', () => {}); + mws = ws; +}); + + +let executionAccount = 0 + +web3.eth.filter("latest", function (error, result) { + if (!error) { + try { + let info = web3.eth.getBlock(result); + if (info.transactions.length > 0) { + // // console.log('----------------------------------------------------------------------------------------------'); + // // console.log('NEW BLOCK MINED'); + let toNotify = []; + info.transactions.forEach(transactionHash => { + // // console.log("TRANSACTION ", transRec); + //// console.log(web3.eth.estimateGas({from: web3.eth.accounts[0], to: transactionHash, amount: web3.toWei(1, "ether")})) + + let transRec = web3.eth.getTransactionReceipt(transactionHash); + let tranInfo = { 'hash': transactionHash, + 'blockNumber': transRec.blockNumber, + 'gas': transRec.gasUsed, + 'cumulGas': transRec.cumulativeGasUsed } + + + // if(toPrint.length > 0 && toPrint === transactionHash) { + // // console.log('Gas :' + tI + " " + transRec.gasUsed); + // toPrint = ''; + // tI = 0; + // } + + if(pendingTrans.has(tranInfo.hash)) { + toNotify.push(tranInfo); + /* if(transRec.logs && transRec.logs.length > 0) { + console.log(transRec.logs) + console.log('-----------------------------------------------------------------') + } */ + } + + /* if(!bindingOpTransactions.has(tranInfo.hash)) { + transRec.logs.forEach(logElem => { + if (workListInstances.has(logElem.address) && toNotify.indexOf(logElem.address) < 0) { + //// console.log("LOG ELEMENT ", logElem); + // console.log('WorkList', workListInstances); + toNotify.push(workListInstances.get(logElem.address)); + } + }) + } */ + //// console.log('----------------------------------------------------------------------------------------------'); + }); + if (toNotify.length > 0) { + //// console.log("Message sent through socket running on port 8090"); + toNotify.forEach(add => { + // console.log(add.hash) + if (mws) + mws.send(JSON.stringify(add), function ack(error) { + }); + }); + //io.emit('message', { type: 'new-message', text: "Updates in Server" }); + } else { + //// console.log("Nothing to notify"); + } + } + } catch(ex) { } + } +}); + + +///////////////////////////////////////////////////////// +/// CATERPILLAR INTERPRETER OPERATIONS /// +///////////////////////////////////////////////////////// + +let contractsInfo: any; +let processInfo: SubProcessInfo; +let deployedContracts: Map = new Map(); + +let pendingTrans: Map = new Map(); + + +models.post('/interpreter', (req, res) => { + instantiateContract('BPMNInterpreter:BPMNInterpreter') + .then((result) => { + deployedContracts.set('interpreter', result.contract); + res.status(200).send({'contract': result.contract.address, 'gas': result.gas}); + }) + .catch(error => { + res.status(400).send(error); + }) +}); + +models.post('/interpreter/models', (req, res) => { + console.log('Parsig BPMN Model and Generating IData Smart Contracts ...'); + parseBpmnModel(req.body.bpmn) + .then((procInfo: SubProcessInfo) => { + console.log('IFlow information collected ...') + compileContracts(procInfo) + .then((result) => { + console.log(result); + // Deploying Interpreter + instantiateContract('BPMNInterpreter:BPMNInterpreter') + .then(interpreter => { + deployedContracts.set('interpreter', interpreter.contract); + // Deploying IFlow Node + instantiateContract('IFlow:IFlowImpl') + .then(iFlow => { + deployedContracts.set('iFlow', iFlow.contract); + // Deploying Factory + instantiateContract(procInfo.procId+ 'Factory:'+ procInfo.procId + 'Factory') + .then(iFactory => { + deployedContracts.set('iFactory', iFactory.contract); + addFactory() + .then(factoryRes => { + console.log('FactoryTrans: ' + factoryRes); + addInterpreter() + .then(interpreterRes => { + // printIFlow(); + res.status(200).send( + { + iFlow: {address: iFlow.contract.address, gas: iFlow.gas}, + interpreter: {address: interpreter.contract.address, gas: interpreter.gas}, + iFactory: {address: iFactory.contract.address, gas: iFactory.gas}, + toRegister: elementsToRegister(), + elementNames: elementNames() + } + ); + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch((error) => { + res.status(400).send(error); + }) + }) + .catch(error => { + res.status(400).send(error); + }); +}); + +models.get('/interpreter/models', (req, res) => { + // Retrieves the {hashes, names} of the models registered in Process Repository +}); + +models.get('/interpreter/models/:mHash', (req, res) => { + // Retrieves all the metadata of a given model (hash) +}); + +models.post('/i-flow', (req, res) => { + instantiateContract('IFlow:IFlowImpl') + .then((result) => { + deployedContracts.set('i-flow', result.contract); + res.status(200).send({'contract': result.contract.address, 'gas': result.gas}); + }) + .catch(error => { + res.status(400).send(error); + }) +}); + +models.get('/i-flow/:cfAddress', (req, res) => { + // Updating sub-processes into an iflow node +}); + +models.patch('/i-flow/element/:cfAddress', (req, res) => { + // Updating elements into an iflow node +}); + +models.patch('/i-flow/child/:cfAddress', (req, res) => { + // Updating sub-processes into an iflow node +}); + +models.patch('/i-flow/factory/:cfAddress', (req, res) => { + // Updating sub-processes into an iflow node +}); + +// Creation of a new Process Case +models.post('/i-flow/p-cases/:cfAddress', (req, res) => { + let cfAddress = abiCoder.encodeParameter('address', req.params.cfAddress); + let interpreter = deployedContracts.get('interpreter'); + interpreter.createInstance(cfAddress, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + pendingTrans.set(result.toString(), 'pCase: ' + cfAddress); + console.log(result.toString()); + + let myEvent = interpreter.NewCaseCreated({ + fromBlock: 0, + toBlock: 'latest' + }); + myEvent.watch((errEvt, resEvt) => { + if (!errEvt) { + if (resEvt && resEvt.transactionHash === result && resEvt.event === 'NewCaseCreated') { + myEvent.stopWatching(); + let pCase = resEvt.args.pCase.toString(); + console.log('pCase: ', pCase); + console.log('Gas: ', web3.eth.getTransactionReceipt(resEvt.transactionHash).gasUsed); + console.log('....................................................................'); + + res.status(200).send({pCase: pCase, gas: web3.eth.getTransactionReceipt(resEvt.transactionHash).gasUsed }); + + } + } else { + console.log('ERROR ', errEvt); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send(errEvt); + } + }); + } + else { + // console.log(error) + res.status(400).send(error); + } + }) +}); + + +models.get('/i-flow/p-cases/:cfAddress', (req, res) => { + // Retrieves the process cases created for a given iFlow node +}); + +// Retrives the state of a process case (started activities) +models.get('/i-data/:pcAddress', (req, res) => { + res.status(200).send({enabled: getProcessState(req.params.pcAddress)}); +}); + +// Checks-out a task in a given process case +models.get('/i-data/:pcAddress/i-flow/:eIndex', (req, res) => { + // +}); + +// Checks-in a task in a given process case +models.patch('/i-data/:pcAddress/i-flow/:eIndex', (req, res) => { + let pCase = req.params.pcAddress; + let cName = processInfo.procId + 'Data:' + processInfo.procId + 'Data'; + + let contract = web3.eth.contract(JSON.parse(contractsInfo[cName].interface)).at(pCase); + + let reqId = req.params.eIndex; + let inputParams = req.body.inputParameters; + let realParameters = []; + + realParameters = inputParams.length > 0 ? [reqId].concat(inputParams) : [reqId]; + + contract['checkIn'][joinTypes(+reqId)].apply(this, realParameters.concat({ + from:web3.eth.accounts[0], + gas: 4700000 + }, (error, result) => { + if (error) { + console.log('ERROR: ' + error); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Error'); + } else { + pendingTrans.set(result.toString(), 'checkIn: ' + reqId); + //console.log(`TRANSACTION: ${result}, PENDING !!!`); + //console.log('----------------------------------------------------------------------------------------------'); + res.status(200).send({transactionHash: result}); + } + })); +}); + + + + + + + + + + + + + + + + +let printIFlow = () => { + processInfo.iflow.nodeIndexMap.forEach((eInd, eId) => { + console.log('eInd: ', eInd); + console.log('eName: ', processInfo.iflow.elementInfo.get(eInd).eName); + console.log('typeInfo: ', processInfo.iflow.elementInfo.get(eInd).typeInfo); + console.log('preC: ', processInfo.iflow.elementInfo.get(eInd).preC); + console.log('postC: ', processInfo.iflow.elementInfo.get(eInd).postC); + console.log('next: ', processInfo.iflow.elementInfo.get(eInd).next); + console.log('----------------------------------------------') + }); +} + + + + +// Registers an element into IFlow node +models.post('/i-flow/element/:eInd', (req, res) => { + // FOR EXPERIMENTS ONLY + // Extend method to receive the input parameters as JSON in the body. + // Actual uri: /i-flow/element/:cfAddress + let eInd: number = +req.params.eInd + let eInfo: ElementIFlow = processInfo.iflow.elementInfo.get(eInd); + let preC = abiCoder.encodeParameter('uint256', eInfo.preC); + let postC= abiCoder.encodeParameter('uint256', eInfo.postC); + let typeInfo = abiCoder.encodeParameter('uint256', eInfo.typeInfo); + registerElement(eInd, preC, postC, typeInfo, 'elem', eInfo.next) + .then((result) => { + pendingTrans.set(result.toString(), 'Register: ' + eInd); + res.status(200).send({ transactionHash: result }); + }) + .catch((error) => { + res.status(400).send(error); + }) +}) + + + + + + +let getProcessState = (pCase: string) => { + + let cName = processInfo.procId + 'Data:' + processInfo.procId + 'Data'; + let iData = web3.eth.contract(JSON.parse(contractsInfo[cName].interface)).at(pCase); + let iFlow = deployedContracts.get('iFlow'); + + let startedActivities = iData.getStartedActivities.call().toString(2).split('').reverse(); + let tokensonEdges = iData.getMarking.call().toString(2).split('').reverse(); + let eCandidates = processInfo.iflow.nodeIndexMap.size; + let enabledTasks = new Array(); + + for (let eInd = 1; eInd <= eCandidates; eInd++) { + let preC = iFlow.getPreCond.call(eInd).toString(2).split('').reverse(); + let postC = iFlow.getPostCond.call(eInd).toString(2).split('').reverse(); + let typeInfo = iFlow.getTypeInfo.call(eInd).toString(2).split('').reverse(); + + if(typeInfo[0] === '1' && typeInfo[3] === '1' && (typeInfo[11] === '1' || typeInfo[14] === '1')) { + for(let i = 0; i < preC.length; i++) { + if(preC[i] === '1' && i < tokensonEdges.length && tokensonEdges[i] === '1') { + enabledTasks.push(eInd); + break; + } + } + } + } + return enabledTasks; +} + +let joinTypes = (eInd: number) => { + let params: Array = processInfo.iData.inParams.get(eInd); + let res: string = 'uint256'; + params.forEach(param => { + res += param.type === 'uint' ? ',uint256' : `,${param.type}`; + }); + return res; +} + +let registerElement = (eInd, preC, postC, typeInfo, eId, nextElem) => { + return new Promise((resolve, reject) => { + let iFlow = deployedContracts.get('iFlow'); + iFlow.setElement(eInd, preC, postC, typeInfo, eId, nextElem, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + resolve(result) + } + else { + reject(error); + } + }) + + }); +} + +let elementsToRegister = () => { + let res = []; + processInfo.iflow.nodeIndexMap.forEach((eInd, eId) => { + res[eInd] = eId; + }); + return res; +} + +let elementNames = () => { + let res = []; + processInfo.iflow.nodeIndexMap.forEach((eInd, eId) => { + res[eInd] = processInfo.iflow.elementInfo.get(eInd).eName; + }); + return res; +} + + +let addFactory = () => { + return new Promise((resolve, reject) => { + let iFlow = deployedContracts.get('iFlow'); + let iFactoryAddr = deployedContracts.get('iFactory').address; + iFlow.setFactoryInst(iFactoryAddr, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + resolve(result) + } + else { + console.log('ERROR ', error); + reject(error); + } + }) + }) +} + +let addInterpreter = () => { + return new Promise((resolve, reject) => { + let iFlow = deployedContracts.get('iFlow'); + let interpreterAddr = deployedContracts.get('interpreter').address; + iFlow.setInterpreterInst(interpreterAddr, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + resolve(result) + } + else { + console.log('ERROR ', error); + reject(error); + } + }) + }) +} + +let compileContracts = (procInfo: SubProcessInfo) => { + return new Promise((resolve, reject) => { + let input = { + 'IFlow': fs.readFileSync('./src/models/interpreter/smart_contracts/IFlow.sol', 'utf8'), + 'IData': fs.readFileSync('./src/models/interpreter/smart_contracts/IData.sol', 'utf8'), + 'IFactory': fs.readFileSync('./src/models/interpreter/smart_contracts/IFactory.sol', 'utf8'), + 'BPMNInterpreter': fs.readFileSync('./src/models/interpreter/smart_contracts/BPMNInterpreter.sol', 'utf8'), + // 'BindingAccessControl' : fs.readFileSync('./src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol', 'utf8') + }; + + let queue: Array = new Array(); + queue.push(procInfo); + + while(queue.length > 0) { + let topProc: SubProcessInfo = queue.pop(); + console.log(topProc.iData.iDataSolidity) + console.log('---------------------------------') + input[topProc.procId + 'Data'] = topProc.iData.iDataSolidity; + input[topProc.procId + 'Factory'] = topProc.iData.factorySolidity; + for(let i = 0; i < topProc.children.length; i++) + queue.push(topProc.children[i]); + } + console.log('Compiling IData Contracts ...'); + + let output = solc.compile({sources: input}, 1); + if (Object.keys(output.contracts).length === 0) { + console.log('Compilation Errors In IData Ssmart Contracts'); + console.log(output.errors); + console.log('----------------------------------------------------------------------------------------------'); + reject('Compilation Errors In IData Ssmart Contracts'); + } else { + contractsInfo = output.contracts; + processInfo = procInfo; + resolve('IData Contracts Generated And Compiled Successfully. '); + } + }) +} + +let instantiateContract = (key: string) => { + return new Promise((resolve, reject) => { + let contract = web3.eth.contract(JSON.parse(contractsInfo[key].interface)); + let bytecode = contractsInfo[key].bytecode; + let gasEstimate = web3.eth.estimateGas({data: bytecode}); + contract.new( + {from: web3.eth.accounts[0], data: "0x" + bytecode, gas: gasEstimate + 50000}, + (errF, contractF) => { + if (errF) { + console.log(`${key} instance creation failed`); + console.log('ERROR ', errF); + reject(errF); + } else if (contractF.address) { + let gasUsed = web3.eth.getTransactionReceipt(contractF.transactionHash).gasUsed; + console.log(`${key} CONTRACT DEPLOYED and RUNNING at ` + contractF.address.toString()); + console.log('GAS USED: ', gasUsed); + console.log('............................................') + resolve({'contract': contractF, 'gas': gasUsed}); + } + }); + }); +} + + +let workListInstances: Map = new Map(); +let bindingOpTransactions: Map = new Map(); + +let processRegistryContract: any = undefined; + +// Querying for every contract all the created instances + +models.get('/processes', (req, res) => { + console.log('QUERYING ALL ACTIVE CONTRACTS'); + let actives = []; + if(processRegistryContract) { + let registeredInstances = processRegistryContract.allInstances.call(); + registeredInstances.forEach(instance => { + let repoID = web3.toAscii(processRegistryContract.bundleFor.call(instance)).toString().substr(0, 24); + repoSchema.find({_id: repoID}, + (err, repoData) => { + if (err) { + res.status(404).send([]); + return; + } else { + if(repoData.length > 0) { + console.log({id: repoID, name: repoData[0].rootProcessName, address: instance}); + actives.push({id: repoID, name: repoData[0].rootProcessName, address: instance}); + if (actives.length === registeredInstances.length) { + res.status(200).send(actives); + return; + } + } + } + }) + }) + } else { + res.status(404).send([]); + } +}); + +// Querying all registered (root) process in the repository + +models.get('/models', (req, res) => { + console.log('QUERYING REGISTERED MODELS'); + let actives = []; + if(processRegistryContract) { + repoSchema.find({'bpmnModel': {$ne: 'empty'}}, + (err, repoData) => { + if (err) + res.send([]); + else { + repoData.forEach(data => { + if(web3.toAscii(processRegistryContract.childrenFor(data._id.toString(), 0)).toString().substr(0, 24) === data._id.toString()) { + console.log({id: data._id, name: data.rootProcessName}); + actives.push({ + id: data._id, + name: data.rootProcessName, + bpmn: data.bpmnModel, + solidity: data.solidityCode + }) + } + }); + console.log('----------------------------------------------------------------------------------------------'); + res.send(actives); + } + }); + } else { + res.send([]); + console.log('----------------------------------------------------------------------------------------------'); + } +}); + +////////////////////////////////////////////////////////////////////// +/// CONFIGURATION (ProcessRegistry) REGISTRATION operations /// +////////////////////////////////////////////////////////////////////// + +models.post('/registry', (req, res) => { + console.log('DEPLOYING PROCESS RUNTIME REGISTRY ...'); + try { + let input = { + 'AbstractFactory': fs.readFileSync('./src/models/abstract/AbstractFactory.sol', 'utf8'), + 'ProcessRegistry': fs.readFileSync('./src/models/abstract/ProcessRegistry.sol', 'utf8') + }; + + console.log('============================================='); + console.log("SOLIDITY CODE"); + console.log('============================================='); + console.log(input['ProcessRegistry']); + console.log('....................................................................'); + + let output = solc.compile({sources: input}, 1); + if (Object.keys(output.contracts).length === 0) { + res.status(400).send('COMPILATION ERROR IN RUNTIME REGISTRY SMART CONTRACTS'); + console.log('COMPILATION ERROR IN SMART CONTRACTS'); + console.log(output.errors); + console.log('----------------------------------------------------------------------------------------------'); + return; + } + + console.log('PROCESS RUNTIME REGISTRY COMPILED SUCCESSFULLY'); + console.log('CREATING RUNTIME REGISTRY INSTANCE ... '); + + let ProcContract = web3.eth.contract(JSON.parse(output.contracts['ProcessRegistry:ProcessRegistry'].interface)); + ProcContract.new( + { + from: web3.eth.accounts[executionAccount], + data: "0x" + output.contracts['ProcessRegistry:ProcessRegistry'].bytecode, + gas: 4700000 + }, + (err, contract) => { + if (err) { + console.log(`ERROR: ProcessRegistry instance creation failed`); + console.log('RESULT ', err); + res.status(403).send(err); + } else if (contract.address) { + registrySchema.create( + { + address: contract.address, + solidityCode: input['ProcessRegistry'], + abi: output.contracts['ProcessRegistry:ProcessRegistry'].interface, + bytecode: output.contracts['ProcessRegistry:ProcessRegistry'].bytecode, + }, + (err, repoData) => { + if (err) { + console.log('Error ', err); + console.log('----------------------------------------------------------------------------------------------'); + // registerModels(currentIndex, sortedElements, createdElementMap, modelInfo, contracts, res); + } + else { + processRegistryContract = contract; + let registryGas = web3.eth.getTransactionReceipt(contract.transactionHash).gasUsed; + let idAsString = repoData._id.toString(); + console.log("Process Registry DEPLOYED and RUNNING at " + processRegistryContract.address.toString()); + console.log('GAS USED: ', registryGas); + console.log('REPO ID: ', idAsString); + res.status(200).send({ 'address': processRegistryContract.address.toString(), gas: registryGas, repoId: idAsString}); + console.log('----------------------------------------------------------------------------------------------'); + } + }) + } + }); + } catch (e) { + console.log("Error: ", e); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send(e); + } +}); + +models.post('/registry/load', (req, res) => { + console.log('LOADING PROCESS RUNTIME REGISTRY ...'); + if(web3.isAddress(req.body.from)) { + registrySchema.find({address: req.body.from}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + processRegistryContract = web3.eth.contract(JSON.parse(repoData[0].abi)).at(req.body.from); + console.log('Registry Loaded Successfully'); + res.status(200).send('Registry Loaded Successfully'); + console.log('----------------------------------------------------------------------------------------------'); + } else { + console.log("Error: Registry NOT Found"); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Registry NOT Found'); + return; + } + }) + } else { + registrySchema.find({_id: req.body.from}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + processRegistryContract = web3.eth.contract(JSON.parse(repoData[0].abi)).at(repoData[0].address); + console.log('Registry Loaded Successfully'); + res.status(200).send('Registry Loaded Successfully'); + console.log('----------------------------------------------------------------------------------------------'); + } else { + console.log("Error: Registry NOT Found"); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Registry NOT Found'); + return; + } + }) + } +}); + + +////////////////////////////////////////////////////////////////////// +/// ROLE DYNAMIC BINDING operations /// +////////////////////////////////////////////////////////////////////// + +models.post('/resources/policy', (req, res) => { + console.log('GENERATING AND DEPLOYING BINDING POLICY CONTRACTS ...'); + if (processRegistryContract === undefined) { + console.log('ERROR: No Runtime Registry Available'); + console.log('----------------------------------------------------------------------------------------------'); + res.status(404).send('Error: Runtime Registry Not Found'); + } else { + console.log('GENERATING SMART CONTRACTS FROM BINDING POLICY ...'); + let resourceModel = req.body.model; + let parsingResult = generatePolicy(resourceModel, 'BindingPolicy'); + parsingResult + .then((policy) => { + let input = { + 'BindingAccessControl': fs.readFileSync('./src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol', 'utf8') + }; + + input['BindingPolicy'] = policy.solidity; + + console.log('============================================='); + console.log("SOLIDITY CODE"); + console.log('============================================='); + Object.keys(input).forEach(key => { + console.log(input[key]); + }); + console.log('....................................................................'); + + let output = solc.compile({sources: input}, 1); + + if (Object.keys(output.contracts).length === 0) { + res.status(400).send('COMPILATION ERROR IN POLICY CONTRACTS'); + console.log('COMPILATION ERROR IN POLICY CONTRACTS'); + console.log(output.errors); + console.log('----------------------------------------------------------------------------------------------'); + return; + } + + console.log('POLICY CONTRACTS GENERATED AND COMPILED SUCCESSFULLY'); + + let ProcContract = web3.eth.contract(JSON.parse(output.contracts['BindingPolicy:BindingPolicy_Contract'].interface)); + ProcContract.new( + { + from: web3.eth.accounts[executionAccount], + data: "0x" + output.contracts['BindingPolicy:BindingPolicy_Contract'].bytecode, + gas: 4700000 + }, + (err, contract) => { + if (err) { + console.log(`ERROR: PolicyContract instance creation failed`); + console.log('RESULT ', err); + res.status(403).send(err); + } else if (contract.address) { + + let indexToRole = []; + for (let [role, index] of policy.roleIndexMap) { + indexToRole[index] = role; + } + policySchema.create( + { + address: contract.address, + model: req.body.model, + solidityCode: input['BindingPolicy'], + abi: output.contracts['BindingPolicy:BindingPolicy_Contract'].interface, + bytecode: output.contracts['BindingPolicy:BindingPolicy_Contract'].bytecode, + indexToRole: indexToRole, + accessControlAbi: output.contracts['BindingAccessControl:BindingAccessControl'].interface, + accessControlBytecode: output.contracts['BindingAccessControl:BindingAccessControl'].bytecode, + }, + (err, repoData) => { + if (err) { + console.log('Error ', err); + console.log('----------------------------------------------------------------------------------------------'); + // registerModels(currentIndex, sortedElements, createdElementMap, modelInfo, contracts, res); + } + else { + let idAsString = repoData._id.toString(); + let policyGas = web3.eth.getTransactionReceipt(contract.transactionHash).gasUsed; + console.log("Policy CREATED and RUNNING at " + contract.address.toString()); + console.log('GAS USED: ', policyGas); + console.log('Policy Id: ', idAsString); + console.log('Role\'s indexes: ', policy.roleIndexMap); + console.log("............................................."); + res.status(200).send({address: contract.address.toString(), gas: policyGas, repoId: idAsString }); + console.log('----------------------------------------------------------------------------------------------'); + } + }) + } + }); + }) + .catch((err) => { + res.status(200).send({ 'Error': 'Error Parsing' }); + console.log('----------------------------------------------------------------------------------------------'); + }) + } +}); + +models.post('/resources/task-role', (req, res) => { + if(processRegistryContract === undefined) { + console.log('ERROR: Runtime Registry NOT FOUND.'); + res.status(404).send({ 'Error': 'Runtime Registry NOT FOUND. Please, Create/Load a Registry.' }); + console.log('----------------------------------------------------------------------------------------------'); + } else { + if(web3.isAddress(req.body.policyId)) { + policySchema.find({address: req.body.policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let processData: Map> = new Map(); + searchRepository(0, [req.body.rootProc], processData, res, req.body.policyId, findRoleMap(repoData[0].indexToRole)); + } else { + console.log("Error: Binding Policy NOT Found"); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Binding Policy NOT Found'); + return; + } + }) + } else { + policySchema.find({_id: req.body.policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let processData: Map> = new Map(); + searchRepository(0, [req.body.rootProc], processData, res, req.body.policyId, findRoleMap(repoData[0].indexToRole)); + } else { + console.log("Error: Binding Policy NOT Found"); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Binding Policy NOT Found'); + return; + } + }) + } + } +}); + +models.get('/resources/:role/:procAddress', (req, res) => { + if (!web3.isAddress(req.params.procAddress)) { + res.status(200).send({'state' : 'INVALID INPUT PROCESS ADDRESS'}); + } else if(processRegistryContract === undefined) { + res.status(200).send({'state' : 'UNDEFINED PROCESS REGISTRY'}); + } else { + let _policyId = web3.toAscii(processRegistryContract.bindingPolicyFor.call(req.params.procAddress)).toString().substr(0, 24); + policySchema.find({_id: _policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let roleIndexMap = findRoleMap(repoData[0].indexToRole); + if(!roleIndexMap.has(req.params.role)) { + console.log('UNDEFINED INPUT ROLE'); + res.status(200).send({'state' : 'UNDEFINED INPUT ROLE'}); + } else { + let accessControlAddr = processRegistryContract.findRuntimePolicy.call(req.params.procAddress); + if(accessControlAddr.toString() === '0x0000000000000000000000000000000000000000') { + console.log('UNDEFINED ACESS CONTROL CONTRACT'); + res.status(200).send({'state' : 'UNDEFINED ACESS CONTROL CONTRACT'}); + } else { + let _runtimePolicyContract = web3.eth.contract(JSON.parse(repoData[0].accessControlAbi)).at(accessControlAddr); + let result = _runtimePolicyContract.roleState.call(roleIndexMap.get(req.params.role), req.params.procAddress); + if(result.c[0] === 0) { + console.log(`${req.params.role} is UNBOUND`) + res.status(200).send({'state' : 'UNBOUND'}); + } else if(result.c[0] === 1) { + console.log(`${req.params.role} is RELEASING`) + res.status(200).send({'state' : 'RELEASING'}); + } else if(result.c[0] === 2) { + console.log(`${req.params.role} is NOMINATED`) + res.status(200).send({'state' : 'NOMINATED'}); + } else if(result.c[0] === 3) { + console.log(`${req.params.role} is BOUND`) + res.status(200).send({'state' : 'BOUND'}); + } else { + console.log('UNDEFINED STATE'); + res.status(200).send({'state' : 'UNDEFINED'}); + } + } + } + } else { + console.log('UNDEFINED POLICY CONTRACT'); + res.status(200).send({'state' : 'UNDEFINED POLICY CONTRACT'}); + return; + } + }); + } + console.log('----------------------------------------------------------------------------------------------'); +}); + +let validateInput = (rNominator: string, rNominee: string, roleIndexMap, res: any) => { + if(!roleIndexMap.has(rNominee)) { + console.log(`Error Nominee Role [${rNominee}] NOT FOUND`); + res.status(404).send({ 'Error': `Nominee Role [${rNominee}] NOT FOUND` }); + console.log('----------------------------------------------------------------------------------------------'); + return false; + } else if(!roleIndexMap.has(rNominator)) { + console.log(`Error Nominee Role [${rNominee}] NOT FOUND`); + res.status(404).send({ 'Error': `Nominee Role [${rNominee}] NOT FOUND` }); + console.log('----------------------------------------------------------------------------------------------'); + return false; + } + return true; +} + +let findRoleMap = (repoArr) => { + let roleInedexMap: Map = new Map(); + for (let i = 1; i < repoArr.length; i++) + if(repoArr[i]) + roleInedexMap.set(repoArr[i], i); + return roleInedexMap; +} + +let verifyAddress = (address: string, actor: string, res: any) => { + if (!web3.isAddress(address)) { + console.log('Error: ', `Invalid ${actor} Address [${address}]`); + res.status(400).send(`Invalid Nominator Address [${address}]`); + console.log('----------------------------------------------------------------------------------------------'); + return false; + } + return true; +} + + +models.post('/resources/nominate', (req, res) => { + if(processRegistryContract === undefined) { + res.status(404).send({'state' : 'UNDEFINED PROCESS REGISTRY'}); + } else { + let _policyId = web3.toAscii(processRegistryContract.bindingPolicyFor.call(req.body.pCase)).toString().substr(0, 24); + policySchema.find({_id: _policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let roleIndexMap = findRoleMap(repoData[0].indexToRole); + if(validateInput(req.body.rNominator, req.body.rNominee, roleIndexMap, res)) { + if (verifyAddress(req.body.nominator, 'Nominator', res) && + verifyAddress(req.body.nominee, 'Nominee', res) && + verifyAddress(req.body.pCase, 'Process Case', res)) { + let accessControlAddr = processRegistryContract.findRuntimePolicy.call(req.body.pCase); + if(accessControlAddr.toString() !== '0x0000000000000000000000000000000000000000') { + console.log(`${req.body.rNominator}[${req.body.nominator}] is nominating ${req.body.rNominee}[${req.body.nominee}]`); + console.log(`Process Case: ${req.body.pCase}`); + let _runtimePolicyContract = web3.eth.contract(JSON.parse(repoData[0].accessControlAbi)).at(accessControlAddr); + _runtimePolicyContract.nominate( + roleIndexMap.get(req.body.rNominator), + roleIndexMap.get(req.body.rNominee), + req.body.nominator, + req.body.nominee, + req.body.pCase, + { + from: req.body.nominator, + gas: 4700000 + }, + (error, result) => { + if (result) { + console.log(`SUCCESS: ${req.body.nominator} nominated ${req.body.nominee}`); + console.log(`Transaction Hash: ${result}`) + console.log('----------------------------------------------------------------------------------------------'); + bindingOpTransactions.set(result, 0); + res.status(200).send({'transactionHash': result}); + } + else { + console.log('ERROR', 'Nomination REJECTED by the Binding Policy'); + console.log('----------------------------------------------------------------------------------------------'); + res.status(404).send({'ERROR': error}); + } + }) + } else { + console.log(`Process Instance NOT FOUND.`); + res.status(404).send({ 'Error': `Process Instance NOT FOUND. The nomination of an actor must occurr afterthe process deployment.` }); + console.log('----------------------------------------------------------------------------------------------'); + } + } + } + } else { + console.log('UNDEFINED POLICY CONTRACT'); + res.status(400).send({'state' : 'UNDEFINED POLICY CONTRACT'}); + return; + } + }) + } +}) + +models.post('/resources/release', (req, res) => { + if(processRegistryContract === undefined) { + res.status(404).send({'state' : 'UNDEFINED PROCESS REGISTRY'}); + } else { + let _policyId = web3.toAscii(processRegistryContract.bindingPolicyFor.call(req.body.pCase)).toString().substr(0, 24); + policySchema.find({_id: _policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let roleIndexMap = findRoleMap(repoData[0].indexToRole); + if(validateInput(req.body.rNominator, req.body.rNominee, roleIndexMap, res)) { + if (verifyAddress(req.body.nominator, 'Nominator', res) && + verifyAddress(req.body.pCase, 'Process Case', res)) { + let accessControlAddr = processRegistryContract.findRuntimePolicy.call(req.body.pCase); + if(accessControlAddr.toString() !== '0x0000000000000000000000000000000000000000') { + console.log(`${req.body.rNominator}[${req.body.nominator}] is releasing ${req.body.rNominee}[${req.body.nominee}]`); + console.log(`Process Case: ${req.body.pCase}`); + let _runtimePolicyContract = web3.eth.contract(JSON.parse(repoData[0].accessControlAbi)).at(accessControlAddr); + _runtimePolicyContract.release( + roleIndexMap.get(req.body.rNominator), + roleIndexMap.get(req.body.rNominee), + req.body.nominator, + req.body.pCase, + { + from: req.body.nominator, + gas: 4700000 + }, + (error, result) => { + if (result) { + console.log(`SUCCESS: ${req.body.nominator} released ${req.body.nominee}`); + console.log(`Transaction Hash: ${result}`) + console.log('----------------------------------------------------------------------------------------------'); + bindingOpTransactions.set(result, 0); + res.status(200).send({'transactionHash': result}); + } + else { + console.log('ERROR', 'Release REJECTED by the Binding Policy'); + res.status(400).send({'ERROR': error}); + } + }) + } else { + console.log(`Process Instance NOT FOUND.`); + res.status(404).send({ 'Error': `Process Instance NOT FOUND. The release of an actor must occurr afterthe process deployment.` }); + console.log('----------------------------------------------------------------------------------------------'); + } + } + } + } else { + console.log('UNDEFINED POLICY CONTRACT'); + res.status(400).send({'state' : 'UNDEFINED POLICY CONTRACT'}); + return; + } + }) + } +}) + +let verifyEndorser = (rEndorser: string, endorser: string, roleIndexMap, res: any) => { + if(!roleIndexMap.has(rEndorser)) { + console.log(`Error Endorser Role [${rEndorser}] NOT FOUND`); + res.status(404).send({ 'Error': `Nominee Role [${rEndorser}] NOT FOUND` }); + console.log('----------------------------------------------------------------------------------------------'); + return false; + } + return verifyAddress(endorser, 'Endorser', res); +} + +models.post('/resources/vote', (req, res) => { + if(processRegistryContract === undefined) { + res.status(404).send({'state' : 'UNDEFINED PROCESS REGISTRY'}); + } else { + let _policyId = web3.toAscii(processRegistryContract.bindingPolicyFor.call(req.body.pCase)).toString().substr(0, 24); + policySchema.find({_id: _policyId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let roleIndexMap = findRoleMap(repoData[0].indexToRole); + if(validateInput(req.body.rNominator, req.body.rNominee, roleIndexMap, res)) { + if (verifyEndorser(req.body.rEndorser, req.body.endorser, roleIndexMap, res) && + verifyAddress(req.body.pCase, 'Process Case', res)) { + let accessControlAddr = processRegistryContract.findRuntimePolicy.call(req.body.pCase); + if(accessControlAddr.toString() !== '0x0000000000000000000000000000000000000000') { + let _runtimePolicyContract = web3.eth.contract(JSON.parse(repoData[0].accessControlAbi)).at(accessControlAddr); + if(req.body.onNomination) { + let voteResult = req.body.isAccepted === "true" ? 'endorsing' : 'rejecting'; + console.log(`${req.body.rEndorser}[${req.body.endorser}] is ${voteResult} nomination of ${req.body.rNominee} by ${req.body.rNominator}`) + console.log(`Process Case: ${req.body.pCase}`) + _runtimePolicyContract.voteN ( + roleIndexMap.get(req.body.rNominator), + roleIndexMap.get(req.body.rNominee), + roleIndexMap.get(req.body.rEndorser), + req.body.endorser, + req.body.pCase, + req.body.isAccepted, + { + from: req.body.endorser, + gas: 4700000 + }, + (error, result) => { + if (result) { + let tp = req.body.isAccepted === 'true' ? 'endorsed' : 'rejected'; + console.log(`SUCCESS: ${req.body.endorser} ${tp} the nomination of ${req.body.nominee}`); + console.log(`Transaction Hash: ${result}`) + console.log('----------------------------------------------------------------------------------------------'); + bindingOpTransactions.set(result, 0); + res.status(200).send({'transactionHash': result}); + } + else { + console.log('ERROR', 'Vote REJECTED by the Binding Policy'); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send({'ERROR': error}); + } + }) + } else { + let voteResult = req.body.isAccepted === "true" ? 'endorsing' : 'rejecting'; + console.log(`${req.body.rEndorser}[${req.body.endorser}] is ${voteResult} release of ${req.body.rNominee} by ${req.body.rNominator}`) + console.log(`Process Case: ${req.body.pCase}`) + _runtimePolicyContract.voteR ( + roleIndexMap.get(req.body.rNominator), + roleIndexMap.get(req.body.rNominee), + roleIndexMap.get(req.body.rEndorser), + req.body.endorser, + req.body.pCase, + req.body.isAccepted, + { + from: req.body.endorser, + gas: 4700000 + }, + (error, result) => { + if (result) { + let tp = req.body.isAccepted === 'true' ? 'endorsed' : 'rejected'; + console.log(`VOTE ACCEPTED: ${req.body.endorser} ${tp} the release of ${req.body.nominee}`); + console.log(`Transaction Hash: ${result}`) + console.log('----------------------------------------------------------------------------------------------'); + bindingOpTransactions.set(result, 0); + res.status(200).send({'transactionHash': result}); + } + else { + console.log('ERROR', 'Vote REJECTED by the Binding Policy'); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send({'ERROR': error}); + } + }) + } + } else { + console.log(`Process Instance NOT FOUND.`); + res.status(404).send({ 'Error': `Process Instance NOT FOUND. The voting of an operation must occurr afterthe process deployment.` }); + console.log('----------------------------------------------------------------------------------------------'); + } + } + } + } else { + console.log('UNDEFINED POLICY CONTRACT'); + res.status(400).send({'state' : 'UNDEFINED POLICY CONTRACT'}); + return; + } + }) + } +}) + +let searchRepository = (top: number, queue: Array, processData: Map>, response, policyId, roleIndexMap) => { + processData.set(queue[top], new Array()); + repoSchema.find({_id: queue[top]}, + (err, repoData) => { + if (err) { + return; + } else { + if(repoData.length > 0) { + let dictionary = repoData[0].indexToElement; + for (let i = 1; i < dictionary.length; i++) { + if(dictionary[i].type === 'Workitem') { + processData.get(queue[top]).push({taskIndex: i, roleIndex: roleIndexMap.get(dictionary[i].role)}); + } else if (dictionary[i].type === 'Separate-Instance') { + queue.push(web3.toAscii(processRegistryContract.childrenFor.call(queue[top], i)).toString().substr(0, 24)); + } + } + if(top < queue.length - 1) + searchRepository(top + 1, queue, processData, response, policyId, roleIndexMap); + else { + let procesRoleContract = generateRoleTaskContract(processData, 'TaskRoleContract', true); + procesRoleContract + .then((solidity) => { + let input = {} + input['TaskRoleContract'] = solidity; + + console.log('============================================='); + console.log("SOLIDITY CODE"); + console.log('============================================='); + console.log(solidity) + + let output = solc.compile({sources: input}, 1); + + if (Object.keys(output.contracts).length === 0) { + response.status(400).send('COMPILATION ERROR IN TASK-ROLE CONTRACT'); + console.log('COMPILATION ERROR IN TASK-ROLE CONTRACT'); + console.log(output.errors); + console.log('----------------------------------------------------------------------------------------------'); + return; + } + + let ProcContract = web3.eth.contract(JSON.parse(output.contracts['TaskRoleContract:TaskRoleContract_Contract'].interface)); + ProcContract.new( + { + from: web3.eth.accounts[executionAccount], + data: "0x" + output.contracts['TaskRoleContract:TaskRoleContract_Contract'].bytecode, + gas: 4700000 + }, + (err, contract) => { + if (err) { + console.log(`ERROR: TASK-ROLE-MAP instance creation failed`); + console.log('RESULT ', err); + response.status(403).send(err); + } else if (contract.address) { + roleTaskSchema.create( + { + address: contract.address, + solidityCode: input['TaskRoleContract'], + abi: output.contracts['TaskRoleContract:TaskRoleContract_Contract'].interface, + bytecode: output.contracts['TaskRoleContract:TaskRoleContract_Contract'].bytecode, + }, + (err, repoData) => { + if (err) { + console.log('Error ', err); + console.log('----------------------------------------------------------------------------------------------'); + } + else { + let idAsString = repoData._id.toString(); + processRegistryContract.relateProcessToPolicy(queue[0], policyId, idAsString, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + let gas = web3.eth.getTransactionReceipt(contract.transactionHash).gasUsed; + console.log("TaskRoleMap CREATED and RUNNING at " + contract.address.toString()); + console.log('GAS USED: ', gas); + console.log('Repo Id: ', idAsString); + response.status(200).send({ address: contract.address.toString(), gas: gas, repoId: idAsString }); + console.log('----------------------------------------------------------------------------------------------'); + } + else { + console.log('ERROR ', error); + response.status(400).send(error); + } + }) + } + }) + } + }); + }) + .catch((err) => { + console.log('Error: process ID ' + queue[top] + ' not found'); + response.status(404).send({ 'Error': 'Process ID not found' }); + console.log('----------------------------------------------------------------------------------------------'); + }) + } + } + } + }) +} + +////////////////////////////////////////////////////////////////////// +/// PROCESS MODEL CONTROL FLOW + WORKLIST operations /// +////////////////////////////////////////////////////////////////////// + +models.post('/models', (req, res) => { + if (processRegistryContract === undefined) { + console.log('ERROR: Runtime Registry NOT FOUND'); + res.status(404).send({'Error': 'Runtime Registry NOT FOUND'}); + console.log('----------------------------------------------------------------------------------------------'); + } else { + console.log('GENERATING SMART CONTRACTS FROM PROCESS MODEL ...'); + let modelInfo: ModelInfo = req.body as ModelInfo; + try { + let cont = parseModel(modelInfo); + cont.then(() => { + let input = { + 'AbstractFactory': fs.readFileSync('./src/models/abstract/AbstractFactory.sol', 'utf8'), + 'AbstractRegistry': fs.readFileSync('./src/models/abstract/AbstractRegistry.sol', 'utf8'), + 'AbstractWorklist': fs.readFileSync('./src/models/abstract/AbstractWorklist.sol', 'utf8'), + 'ProcessRegistry': fs.readFileSync('./src/models/abstract/ProcessRegistry.sol', 'utf8'), + 'AbstractProcess': fs.readFileSync('./src/models/abstract/AbstractProcess.sol', 'utf8'), + 'BindingAccessControl' : fs.readFileSync('./src/models/dynamic_binding/runtime_solidity/BindingAccessControl.sol', 'utf8') + }; + input[modelInfo.id] = modelInfo.solidity; + + console.log('============================================='); + console.log("SOLIDITY CODE"); + console.log('============================================='); + console.log(modelInfo.solidity); + console.log('....................................................................'); + + let output = solc.compile({sources: input}, 1); + if (Object.keys(output.contracts).length === 0) { + res.status(400).send('COMPILATION ERROR IN SMART CONTRACTS'); + console.log('COMPILATION ERROR IN SMART CONTRACTS'); + console.log(output.errors); + console.log('----------------------------------------------------------------------------------------------'); + return; + } + + console.log('CONTRACTS GENERATED AND COMPILED SUCCESSFULLY'); + Object.keys(output.contracts).forEach(key => { + let bytecode = '0x' + output.contracts[key].bytecode; + var gasEstimate = web3.eth.estimateGas({data: bytecode}); + // console.log("............................................."); + // console.log("Contract Name: " + key.split(':')[1]); + // console.log("Gas Estimation: " + gasEstimate); + + }); + console.log('....................................................................'); + console.log('STARTING PROCESS MODEL REGISTRATION ...'); + registerModel(modelInfo, output.contracts, res); + }) + } catch (e) { + console.log("ERROR: ", e); + res.status(400).send(e); + console.log('----------------------------------------------------------------------------------------------'); + } + } +}); + +// Creating a new instance of a registered (root) process + +let caseCreatorMap: Map = new Map(); + +models.post('/models/:bundleId', (req, res) => { + if (verifyAddress(req.body.caseCreator, 'Case Creator', res) && processRegistryContract !== undefined) { + let _taskRoleId = web3.toAscii(processRegistryContract.taskRoleMapFromId.call(req.params.bundleId)).toString().substr(0, 24); + roleTaskSchema.find({_id: _taskRoleId}, + (err, repoDataTaskRole) => { + if (!err && repoDataTaskRole && repoDataTaskRole.length > 0) { + let _policyId = web3.toAscii(processRegistryContract.bindingPolicyFromId.call(req.params.bundleId)).toString().substr(0, 24); + policySchema.find({_id: _policyId}, + (err, repoDataPolicy) => { + if (!err && repoDataPolicy && repoDataPolicy.length > 0) { + let roleIndexMap = findRoleMap(repoDataPolicy[0].indexToRole); + if (!roleIndexMap.has(req.body.creatorRole)) { + console.log('Case Creator Role NOT found'); + res.status(404).send('Case Creator Role NOT found'); + console.log('----------------------------------------------------------------------------------------------'); + } else { + repoSchema.find({_id: req.params.bundleId}, + (err, repoData) => { + if (err) + res.status(404).send('Process model not found'); + else { + console.log("TRYING TO CREATE INSTANCE OF CONTRACT: ", repoData[0].rootProcessID); + let AccessControlContract = web3.eth.contract(JSON.parse(repoDataPolicy[0].accessControlAbi)); + AccessControlContract.new(processRegistryContract.address, repoDataPolicy[0].address, repoDataTaskRole[0].address, + { + from: req.body.caseCreator, + data: "0x" + repoDataPolicy[0].accessControlBytecode, + gas: 4700000 + }, + (err, contract) => { + if (err) { + console.log(`ERROR: BindingAccessControl instance creation failed`); + console.log('RESULT ', err); + res.status(403).send(err); + } else if (contract.address) { + let policyGas = web3.eth.getTransactionReceipt(contract.transactionHash).gasUsed; + console.log("BindingAccessControl Contract DEPLOYED and RUNNING at " + contract.address.toString()); + console.log('Gas Used: ', policyGas); + console.log('....................................................................'); + + processRegistryContract.newBundleInstanceFor(repoData[0]._id.toString(), 0, contract.address, { + from: web3.eth.accounts[executionAccount], + gas: 4500000 + }, + (errNew, resNew) => { + if (!errNew) { + let myEvent = processRegistryContract.NewInstanceCreatedFor({ + fromBlock: 0, + toBlock: 'latest' + }); + myEvent.watch((errEvt, resEvt) => { + if (!errEvt) { + if (resEvt && resEvt.transactionHash === resNew && resEvt.event === 'NewInstanceCreatedFor' && parseInt(resEvt.args.parent.toString(), 16) === 0) { + myEvent.stopWatching(); + let processAddress = resEvt.args.processAddress.toString(); + console.log('Root Process Contract DEPLOYED and RUNNING !!! AT ADDRESS: ', processAddress); + console.log('GAS USED: ', web3.eth.getTransactionReceipt(resEvt.transactionHash).gasUsed); + console.log('....................................................................'); + + contract.nominateCaseCreator(roleIndexMap.get(req.body.creatorRole), req.body.caseCreator, processAddress, { + from: req.body.caseCreator, + gas: 4700000 + }, + (error1, result1) => { + if (result1) { + console.log("Case-creator nominated "); + caseCreatorMap.set(result1, processAddress); + console.log('----------------------------------------------------------------------------------------------'); + res.status(200).send({ + address: processAddress, + gas: web3.eth.getTransactionReceipt(resEvt.transactionHash).gasUsed, + runtimeAddress: contract.address.toString(), + runtimeGas: policyGas, + transactionHash: result1, + }); + } + else { + console.log('ERROR ', error1); + console.log('----------------------------------------------------------------------------------------------'); + res.status(200).send({ERROR: error1}); + } + }) + } + } else { + console.log('ERROR ', errEvt); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send(errEvt); + } + }); + } else { + console.log('ERROR ', errNew); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send(errNew); + } + }); + } + }); + } + }); + } + } else { + console.log('UNDEFINED POLICY CONTRACT'); + res.status(400).send({'state' : 'UNDEFINED POLICY CONTRACT'}); + return; + } + }) + + } else { + console.log('Task-Role Contract NOT found'); + res.status(404).send('Task-Role Contract NOT found'); + console.log('----------------------------------------------------------------------------------------------'); + } + }) + } else + res.status(404).send('Process model not found'); +}); + +// Querying activation for a given process (repository ID provided) + +models.get('/processes/:procAddress', (req, res) => { + let contractAddress = req.params.procAddress; + console.log('QUERYING ACTIVATION FOR CONTRACT:', contractAddress); + if (processRegistryContract) { + let bundleId = web3.toAscii(processRegistryContract.bundleFor.call(contractAddress)).toString().substr(0, 24); + repoSchema.find({_id: bundleId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let workitems = [], + serviceTasks = []; + console.log("CHECKING STARTED ELEMENTS "); + instanceStateFor(0, [contractAddress], repoData[0].bpmnModel, workitems, serviceTasks, res); + } else { + console.log('Instance Not Found'); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send({}); + } + }) + } else { + console.log('Instance Not Found'); + res.status(400).send({}); + } +}); + +// Performing an operation on a started workitem: Execute, Allocate, Revoke. +models.post('/workitems/:worklistAddress/:reqId', (req, res) => { + let worklistAddress = req.params.worklistAddress; + let reqId = req.params.reqId; + if(! web3.isAddress(req.body.user)) { + console.log('Error: ', `Invalid Addres '${req.body.user}' of user trying to perform the operation.`); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send(`Invalid Addres '${req.body.user}' of user trying to perform the operation.`); + } else if (processRegistryContract) { + let bundleId = web3.toAscii(processRegistryContract.worklistBundleFor.call(worklistAddress)).toString().substr(0, 24); + repoSchema.find({_id: bundleId}, + (err, repoData) => { + if (!err && repoData && repoData.length > 0) { + let worklistInstance = web3.eth.contract(JSON.parse(repoData[0].worklistAbi)).at(worklistAddress); + let nodeIndex = worklistInstance.elementIndexFor.call(reqId); + let node = repoData[0].indexToElement[nodeIndex]; + let inputParams = req.body.inputParameters; + let realParameters = []; + let functionName = ''; + + realParameters = inputParams.length > 0 ? [reqId].concat(inputParams) : [reqId]; + console.log(`WANT TO EXECUTE TASK: ${node.name}, ON WORKLIST: ${worklistAddress}`); + functionName = node.name; + + worklistInstance[functionName].apply(this, realParameters.concat({ + from: req.body.user, + gas: 4700000 + }, (error, result) => { + if (error) { + console.log('ERROR: ' + error); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Error'); + } else { + console.log(`TRANSACTION: ${result}, PENDING !!!`); + console.log('----------------------------------------------------------------------------------------------'); + res.status(200).send({transactionHash: result}); + } + })); + } else { + console.log('Error: ', err); + console.log('----------------------------------------------------------------------------------------------'); + res.status(400).send('Error'); + } + }) + } else { + console.log('Error: ', 'Process Registry Undefined'); + res.status(400).send('Process Registry Undefined'); + } +}); + +/////////////// Methods for deploying model ////////////////////// + +// Step 1. Model Registration: Collects the compilation artifacts of the produced models, +// and saves all these metadata as an entry in the Process Repository. + +let registerModel = (modelInfo, contracts, response) => { + // Sorting elements such that children are created first + let queue = [{nodeId: modelInfo.id, nodeName: modelInfo.name, bundleId: '', nodeIndex: 0, bundleParent: '', factoryContract: ''}]; + for (let i = 0; i < queue.length; i++) { + if (modelInfo.controlFlowInfoMap.has(queue[i].nodeId)) { + let cfInfo = modelInfo.controlFlowInfoMap.get(queue[i].nodeId); + let candidates = [cfInfo.multiinstanceActivities, cfInfo.nonInterruptingEvents, cfInfo.callActivities]; + candidates.forEach(children => { + if (children) { + children.forEach((value, key) => { + queue.push({nodeId: key, nodeName: value, bundleId: '', nodeIndex: 0, bundleParent: '', factoryContract: '' }); + }) + } + }) + } + } + queue.reverse(); + let nodeIndexes = new Map(); + for (let i = 0; i < queue.length; i++) + nodeIndexes.set(queue[i].nodeId, i); + console.log('....................................................................'); + console.log('UPDATING COMPILATION ARTIFACTS IN REPOSITORY ...'); + registerModels(0, queue, nodeIndexes, modelInfo, contracts, response); +}; + +let registerModels = (currentIndex, sortedElements, nodeIndexes, modelInfo, contracts, res) => { + let nodeName = sortedElements[currentIndex].nodeName; + let gNodeId = sortedElements[currentIndex].nodeId; + let controlFlowInfo = modelInfo.controlFlowInfoMap.get(gNodeId); + if (modelInfo.globalNodeMap.get(gNodeId).$type === 'bpmn:StartEvent') + controlFlowInfo = modelInfo.controlFlowInfoMap.get(modelInfo.globalNodeMap.get(gNodeId).$parent.id); + if (controlFlowInfo) { + let indexToFunctionName = []; + let childrenSubproc = []; + controlFlowInfo.nodeList.forEach(nodeId => { + let element = modelInfo.globalNodeMap.get(nodeId); + if (controlFlowInfo.nodeList.indexOf(nodeId) >= 0) { + let type = "None"; + let role = "None"; + let indexRole = 0; + if (controlFlowInfo.callActivities.has(nodeId) || controlFlowInfo.multiinstanceActivities.has(nodeId) || controlFlowInfo.nonInterruptingEvents.has(nodeId)) + type = "Separate-Instance"; + else if (element.$type === 'bpmn:ServiceTask') + type = "Service"; + else if (element.$type === 'bpmn:UserTask' || element.$type === 'bpmn:ReceiveTask' || controlFlowInfo.catchingMessages.indexOf(nodeId) >= 0) { + type = "Workitem"; + if(!controlFlowInfo.taskRoleMap.has(nodeId)) + throw 'No role related to User Task: ' + controlFlowInfo.nodeNameMap.get(nodeId); + role = controlFlowInfo.taskRoleMap.get(nodeId); + } + indexToFunctionName[controlFlowInfo.nodeIndexMap.get(nodeId)] = { + name: controlFlowInfo.nodeNameMap.get(nodeId), + id: nodeId, + type: type, + role: role + }; + if (controlFlowInfo.callActivities.has(nodeId) || controlFlowInfo.multiinstanceActivities.has(nodeId) || controlFlowInfo.nonInterruptingEvents.has(nodeId)) { + childrenSubproc.push(nodeId); + sortedElements[nodeIndexes.get(nodeId)].nodeIndex = controlFlowInfo.nodeIndexMap.get(nodeId); + if (controlFlowInfo.externalBundles.has(nodeId)) + sortedElements[nodeIndexes.get(nodeId)].bundleId = controlFlowInfo.externalBundles.get(nodeId); + } + } + }); + let bpmnModel = currentIndex < sortedElements.length - 1 ? 'empty' : modelInfo.bpmn; + let worklistAbi = contracts[`${modelInfo.id}:${nodeName}_Worklist`] ? contracts[`${modelInfo.id}:${nodeName}_Worklist`].interface : 'undefined'; + repoSchema.create( + { + rootProcessID: gNodeId, + rootProcessName: nodeName, + bpmnModel: bpmnModel, + solidityCode: modelInfo.solidity, + abi: contracts[`${modelInfo.id}:${nodeName}_Contract`].interface, + bytecode: contracts[`${modelInfo.id}:${nodeName}_Contract`].bytecode, + indexToElement: indexToFunctionName, + worklistAbi: worklistAbi + }, + (err, repoData) => { + if (err) { + console.log('Error ', err); + // registerModels(currentIndex, sortedElements, createdElementMap, modelInfo, contracts, res); + } + else { + let idAsString = repoData._id.toString(); + sortedElements[currentIndex].bundleId = idAsString; + sortedElements[currentIndex].bundleParent = idAsString; + childrenSubproc.forEach(childId => { + sortedElements[nodeIndexes.get(childId)].bundleParent = idAsString; + }); + console.log(`Compilation artifacts of ${nodeName} updated in repository with id ${idAsString}`); + continueRegistration(currentIndex, sortedElements, nodeIndexes, modelInfo, contracts, res); + } + }) + } else { + continueRegistration(currentIndex, sortedElements, nodeIndexes, modelInfo, contracts, res); + } +}; + +let continueRegistration = (currentIndex, sortedElements, nodeIndexes, modelInfo, contracts, res) => { + if (currentIndex + 1 >= sortedElements.length) { + console.log('....................................................................'); + console.log('RELATING PARENT TO NESTED CHILDREN IN REGISTRY ...'); + createParent2ChildRelation(0, sortedElements, contracts, modelInfo, res); + } + else + registerModels(currentIndex + 1, sortedElements, nodeIndexes, modelInfo, contracts, res); +}; + +let createParent2ChildRelation = (currentIndex, sortedElements, outputContracts, modelInfo, response) => { + processRegistryContract.addChildBundleId(sortedElements[currentIndex].bundleParent, sortedElements[currentIndex].bundleId, sortedElements[currentIndex].nodeIndex, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error, result) => { + if (result) { + console.log(`${sortedElements[currentIndex].nodeName} : ${sortedElements[currentIndex].bundleParent} => (${sortedElements[currentIndex].nodeIndex}), ${sortedElements[currentIndex].bundleId}`); + if (currentIndex + 1 < sortedElements.length) { + createParent2ChildRelation(currentIndex + 1, sortedElements, outputContracts, modelInfo, response); + } else { + console.log('....................................................................'); + let removedCallActivities = []; + sortedElements.forEach(element => { + if (modelInfo.controlFlowInfoMap.has(element.nodeId) || modelInfo.globalNodeMap.get(element.nodeId).$type === 'bpmn:StartEvent') { + removedCallActivities.push(element); + } + }); + if (removedCallActivities.length > 0) { + console.log('DEPLOYING FACTORIES AND UPDATING PROCESS-FACTORY RELATION IN REGISTRY ...'); + registerFactory(0, removedCallActivities, outputContracts, modelInfo, response); + } + } + } + else { + console.log('ERROR ', error); + response.status(400).send(error); + } + }) +}; + +let registerFactory = (currentIndex, sortedElements, outputContracts, modelInfo, response) => { + let entryFactoryName = `${modelInfo.id}:${sortedElements[currentIndex].nodeName}_Factory`; + let FactoryContract = web3.eth.contract(JSON.parse(outputContracts[entryFactoryName].interface)); + FactoryContract.new( + {from: web3.eth.accounts[0], data: "0x" + outputContracts[entryFactoryName].bytecode, gas: 4700000}, + (errF, contractF) => { + if (errF) { + console.log(`ERROR: ${sortedElements[currentIndex].nodeName}_Factory instance creation failed`); + console.log('RESULT ', errF); + response.status(400).send(errF); + } else if (contractF.address) { + console.log(`${sortedElements[currentIndex].nodeName}_Factory running at address ${contractF.address.toString()}`); + continueFactoryRegistration(currentIndex, sortedElements, outputContracts, contractF, modelInfo, response); + } + }); +}; + +let continueFactoryRegistration = (currentIndex, sortedElements, outputContracts, contractF, modelInfo, response) => { + processRegistryContract.registerFactory(sortedElements[currentIndex].bundleId, contractF.address, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error1, result1) => { + if (result1) { + console.log(`${sortedElements[currentIndex].nodeName}_Factory registered SUCCESSFULLY in Process Registry`); + console.log('....................................................................'); + if (currentIndex + 1 < sortedElements.length) { + registerFactory(currentIndex + 1, sortedElements, outputContracts, modelInfo, response); + } else { + console.log('....................................................................'); + console.log('DEPLOYONG WORKLIST CONTRACTS AND UPDATING PROCESS REGISTRY ...'); + createWorklistInstances(0, sortedElements, outputContracts, modelInfo, response); + } + } + else { + console.log('Error ', error1); + response.status(400).send(error1); + } + }) +}; + +let createWorklistInstances = (currentIndex, sortedElements, outputContracts, modelInfo, response) => { + let entryWorklistName = `${modelInfo.id}:${sortedElements[currentIndex].nodeName}_Worklist`; + if (outputContracts[entryWorklistName]) { + let WorklistContract = web3.eth.contract(JSON.parse(outputContracts[entryWorklistName].interface)); + WorklistContract.new( + {from: web3.eth.accounts[0], data: "0x" + outputContracts[entryWorklistName].bytecode, gas: 4700000}, + (errW, contractW) => { + if (errW) { + console.log(`${sortedElements[currentIndex].nodeName}_Worklist instance creation failed`); + console.log('ERROR: ', errW); + response.status(400).send(errW); + } + else if (contractW.address) { + console.log(`${sortedElements[currentIndex].nodeName}_Worklist running at address ${contractW.address.toString()}`); + processRegistryContract.registerWorklist(sortedElements[currentIndex].bundleId, contractW.address, { + from: web3.eth.accounts[0], + gas: 4700000 + }, + (error1, result1) => { + if (result1) { + console.log(`${sortedElements[currentIndex].nodeName}_Worklist registered SUCCESSFULLY in Process Registry`); + console.log('....................................................................'); + sortedElements[currentIndex] = { + nodeId: sortedElements[currentIndex].nodeId, + nodeName: sortedElements[currentIndex].nodeName, + bundleId: sortedElements[currentIndex].bundleId, + bundleParent: sortedElements[currentIndex].bundleParent, + worklist: contractW.address + }; + continueWorklistCreation(currentIndex, sortedElements, outputContracts, modelInfo, response); + } + else { + console.log('ERROR ', error1); + response.status(400).send(error1); + } + }) + } + } + ) + } else { + continueWorklistCreation(currentIndex, sortedElements, outputContracts, modelInfo, response); + } +}; + +let continueWorklistCreation = (currentIndex, sortedElements, outputContracts, modelInfo, response) => { + if (currentIndex + 1 < sortedElements.length) { + createWorklistInstances(currentIndex + 1, sortedElements, outputContracts, modelInfo, response); + } else { + let bundleId = ''; + for (let i = 0; i < sortedElements.length; i++) { + if (sortedElements[i].nodeName === modelInfo.name) { + bundleId = sortedElements[i].bundleId; + break; + } + } + console.log('----------------------------------------------------------------------------------------------'); + response.status(200).send({ + id: bundleId, + name: modelInfo.name, + bpmn: modelInfo.bpmn, + solidity: modelInfo.solidity + }); + } +}; + +///////////////////////////////////////////////////////////////////// + + +let instanceStateFor = (currentIndex, nestedContracts, bpmnModel, workitems, serviceTasks, res) => { + let contractAddress = nestedContracts[currentIndex]; + let bundleId = web3.toAscii(processRegistryContract.bundleFor.call(contractAddress)).toString().substr(0, 24); + repoSchema.find({_id: bundleId}, + (err, repoData) => { + if (err) { + console.log('ERROR ', err); + return []; + } else { + let contractInstance = web3.eth.contract(JSON.parse(repoData[0].abi)).at(contractAddress); + let worklistAddress = contractInstance.getWorklistAddress.call(); + let worklistInstance: any; + if (worklistAddress.toString() !== '0x0000000000000000000000000000000000000000') + worklistInstance = web3.eth.contract(JSON.parse(repoData[0].worklistAbi)).at(worklistAddress); + let dictionary = repoData[0].indexToElement; + + let startedActivities = contractInstance.startedActivities.call().toString(2).split('').reverse(); + for (let index = 0; index < startedActivities.length; index++) { + if (startedActivities[index] === '1') { + if (dictionary[index].type === 'Workitem') { + let reqInd = worklistInstance.workItemsFor.call(index, contractAddress).toString(2).split('').reverse(); + for (let i = 0; i < reqInd.length; i++) { + if (reqInd[i] === '1') { + let notFound = true; + for (let j = 0; j < workitems.length; j++) { + if (workitems[j].elementId === dictionary[index].id && workitems[j].bundleId === bundleId) { + workitems[j].hrefs.push(`/workitems/${worklistAddress}/${i}`); + workitems[j].pCases.push(worklistInstance.processInstanceFor.call(i)); + notFound = false; + break; + } + } + if (notFound) { + workitems.push({ + elementId: dictionary[index].id, + elementName: dictionary[index].name, + input: findParameters(repoData[0].worklistAbi, dictionary[index].name), + bundleId: bundleId, + processAddress: contractAddress, + pCases: [contractAddress], + hrefs: [`/workitems/${worklistAddress}/${i}`] + }); + } + } + } + } else if (dictionary[index].type === 'Service') { + // PENDING + } else if (dictionary[index].type === 'Separate-Instance') { + let startedInstances = contractInstance.startedInstanceIndexFor.call(index).toString(2).split('').reverse(); + let allInstances = contractInstance.allInstanceAddresses.call(); + for (let i = 0; i < startedInstances.length; i++) + if (startedInstances[i] === '1') + nestedContracts.push(allInstances[i]); + } + } + } + if (currentIndex + 1 < nestedContracts.length) + instanceStateFor(currentIndex + 1, nestedContracts, bpmnModel, workitems, serviceTasks, res); + else { + if (workitems.length == 0 && serviceTasks.length == 0) + console.log('No started elements ...'); + else { + workitems.forEach(elem => { + console.log("Element ID: ", elem.elementId); + console.log("Element Name: ", elem.elementName); + console.log("Input Parameters: ", elem.input); + console.log("bundleId: ", elem.bundleId); + console.log("pCases: ", elem.pCases) + console.log("hrefs: ", elem.hrefs); + console.log("..............................................................."); + }) + } + console.log('----------------------------------------------------------------------------------------------'); + res.status(200).send({bpmn: bpmnModel, workitems: workitems, serviceTasks: serviceTasks}); + } + } + }); +}; + +let findParameters = (contractAbi, functionName) => { + let jsonAbi = JSON.parse(contractAbi); + let candidates = []; + jsonAbi.forEach(element => { + if (element.name === functionName) { + candidates = element.inputs; + } + }); + let res = []; + candidates.forEach(element => { + if (element.name && element.name !== 'workitemId') + res.push(element); + }); + return res; +}; + +export default models; diff --git a/v3.0/caterpillar-core/src/models/models.parsers.ts b/v3.0/caterpillar-core/src/models/models.parsers.ts new file mode 100644 index 0000000..7248898 --- /dev/null +++ b/v3.0/caterpillar-core/src/models/models.parsers.ts @@ -0,0 +1,1304 @@ +import * as BpmnModdle from "bpmn-moddle"; +import * as fs from "fs"; +import * as path from "path"; +import * as ejs from "ejs"; +import BigNumber from "bignumber.js"; +import { + ControlFlowInfo, + ModelInfo, + ParameterInfo, + OracleInfo +} from "./definitions"; + +const bpmn2solEJS = fs.readFileSync( + path.join(__dirname, "../../templates") + "/bpmn2sol.ejs", + "utf-8" +); +let bpmn2solTemplate = ejs.compile(bpmn2solEJS); + +const workList2solEJS = fs.readFileSync( + path.join(__dirname, "../../templates") + "/workList2sol.ejs", + "utf-8" +); +let workList2solTemplate = ejs.compile(workList2solEJS); + +let moddle = new BpmnModdle(); +let parseBpmn = bpmnDoc => { + return new Promise((resolve, reject) => { + moddle.fromXML(bpmnDoc, (err, definitions) => { + if (!err) resolve(definitions); + else reject(err); + }); + }); +}; + +let is = (element, type) => element.$instanceOf(type); +let collectControlFlowInfo: (proc: any, globalNodeMap: Map, globalControlFlowInfo: Array) => ControlFlowInfo; +collectControlFlowInfo = (proc: any, + globalNodeMap: Map, + globalControlFlowInfo: Array): ControlFlowInfo => { + let nodeList: Array = []; + let edgeList: Array = []; + let boundaryEvents: Array = []; + let nonBlockingBoundaryEvents: Array = []; + let controlFlowInfo: ControlFlowInfo; + + for (let node of proc.flowElements.filter(e => is(e, "bpmn:FlowNode"))) { + if (is(node, "bpmn:BoundaryEvent")) { + boundaryEvents.push(node.id); + if (node.cancelActivity == false) nonBlockingBoundaryEvents.push(node.id); + } else { + nodeList.push(node.id); + } + globalNodeMap.set(node.id, node); + } + + let sources = [...nodeList]; + + for (let flowEdge of proc.flowElements.filter(e => + is(e, "bpmn:SequenceFlow") + )) { + if (sources.indexOf(flowEdge.targetRef.id) > -1) { + sources.splice(sources.indexOf(flowEdge.targetRef.id), 1); + } + edgeList.push(flowEdge.id); + } + + // Let us remove all source elements from the node list + nodeList = nodeList.filter((node: string) => sources.indexOf(node) < 0); + + if (nonBlockingBoundaryEvents.length > 0) { + let dfs = (sources: string[]) => { + let open = [...sources]; + let nodeList: Array = []; + let edgeList: Array = []; + while (open.length > 0) { + let currId = open.pop(); + let curr = globalNodeMap.get(currId); + nodeList.push(currId); + if (curr.outgoing && curr.outgoing.length > 0) + for (let succEdge of curr.outgoing) { + let succ = succEdge.targetRef; + edgeList.push(succEdge.id); + if (open.indexOf(succ.id) < 0 && nodeList.indexOf(succ.id) < 0) + open.push(succ.id); + } + } + return [nodeList, edgeList]; + }; + let [mainPathNodeList, mainPathEdgeList] = dfs(sources); + let localBoundary = []; + boundaryEvents.forEach(evtId => { + if (nonBlockingBoundaryEvents.indexOf(evtId) < 0) + localBoundary.push(evtId); + }); + if (localBoundary.length > 0) { + let [boundaryNodePath, boundaryEdgePath] = dfs(localBoundary); + boundaryNodePath = boundaryNodePath.filter( + (node: string) => localBoundary.indexOf(node) < 0 + ); + mainPathNodeList = mainPathNodeList.concat(boundaryNodePath); + mainPathEdgeList = mainPathEdgeList.concat(boundaryEdgePath); + } + + // Let us remove all source elements from the node list + mainPathNodeList = mainPathNodeList.filter((node: string) => sources.indexOf(node) < 0); + + controlFlowInfo = new ControlFlowInfo( + proc, + mainPathNodeList, + mainPathEdgeList, + sources, + boundaryEvents + ); + globalControlFlowInfo.push(controlFlowInfo); + for (let eventId of nonBlockingBoundaryEvents) { + let event = globalNodeMap.get(eventId); + if (!mainPathNodeList.find((e: string) => event.attachedToRef.id === e)) { + throw new Error( + "ERROR: Found non-interrupting event which is not attached to a subprocess in the main process path" + ); + } + + let [localNodeList, localEdgeList] = dfs([eventId]); + if ( + mainPathNodeList.filter( + (nodeId: string) => localNodeList.indexOf(nodeId) >= 0 + ).length > 0 + ) + throw new Error( + "ERROR: Non-interrupting event outgoing path is not synchronized and merges with main process path" + ); + + // Let us remove all source elements from the node list + localNodeList = localNodeList.filter((node: string) => sources.indexOf(node) < 0); + + let childControlFlowInfo = new ControlFlowInfo( + event, + localNodeList, + localEdgeList, + [eventId], + [] + ); + childControlFlowInfo.parent = proc; + globalControlFlowInfo.push(childControlFlowInfo); + } + } else { + controlFlowInfo = new ControlFlowInfo( + proc, + nodeList, + edgeList, + sources, + boundaryEvents + ); + globalControlFlowInfo.push(controlFlowInfo); + } + + for (let subprocess of proc.flowElements.filter(e => is(e, "bpmn:SubProcess"))) { + let subprocessControlFlowInfo = collectControlFlowInfo(subprocess, globalNodeMap, globalControlFlowInfo); + subprocessControlFlowInfo.parent = proc; + + if (!(subprocess.loopCharacteristics && subprocess.loopCharacteristics.$type === "bpmn:MultiInstanceLoopCharacteristics")) { + // Subprocess is embedded ... then copy all nodes and edges to the parent process + subprocessControlFlowInfo.isEmbedded = true; + + controlFlowInfo.nodeList = controlFlowInfo.nodeList.concat(subprocessControlFlowInfo.nodeList); + controlFlowInfo.edgeList = controlFlowInfo.edgeList.concat(subprocessControlFlowInfo.edgeList); + controlFlowInfo.boundaryEvents = controlFlowInfo.boundaryEvents.concat(subprocessControlFlowInfo.boundaryEvents); + } + } + if (proc.documentation) { + controlFlowInfo.globalParameters = proc.documentation[0].text; + } + return controlFlowInfo; +}; + +let restrictRelation: Map = new Map(); + + + +let extractParameters = (cad, nodeId, controlFlowInfo) => { + // Extracting Roles from UserTasks functionBody + + let arr = cad.split('@'); + if(arr.length >= 3) { + if(controlFlowInfo != null) + controlFlowInfo.taskRoleMap.set(nodeId, arr[1].trim()); + if(arr[2].length > 1) + cad = arr[2]; + else + return undefined; + } + + // Extracting Information of Oracle from Service Tasks (if aplicable) + let oracle_Data = ""; + for (let j = 0, first = false; j < cad.length; j++) { + if (cad.charAt(j) === "(") { + if (!first) first = true; + else { + cad = cad.substr(j); + break; + } + } + if (cad.charAt(j) === ":") { + oracle_Data = ""; + break; + } + oracle_Data += cad.charAt(j); + } + + // Processing Information of function parameters (both service and user tasks) + cad = cad + .replace("(", " ") + .replace(")", " ") + .trim(); + cad = cad + .replace("(", " ") + .replace(")", " ") + .trim(); + + let firstSplit = cad.split(":"); + if (firstSplit.length > 2) { + let aux = ''; + for (let i = 1; i < firstSplit.length; i++) aux += firstSplit[i]; + firstSplit = [firstSplit[0], aux]; + } + let secondSplit = firstSplit[firstSplit.length - 1].trim().split("->"); + let resMap: Map> = new Map(); + + let inputOutput = [firstSplit[0].trim(), secondSplit[0].trim()]; + let parameterType = ["input", "output"]; + resMap.set("body", [secondSplit[secondSplit.length - 1].trim()]); + + for (let i = 0; i < inputOutput.length; i++) { + let temp = inputOutput[i].split(","); + let res = []; + temp.forEach(subCad => { + let aux = subCad.trim().split(" "); + if (aux[0].trim().length > 0) { + res.push(aux[0].trim()); + res.push(aux[aux.length - 1].trim()); + } + }); + resMap.set(parameterType[i], res); + } + // Updating Information of Oracle in controlFlowInfo + if (controlFlowInfo != null) { + let inParameters: Array = []; + let outParameters: Array = []; + let toIterate = resMap.get('input'); + for (let i = 0; i < toIterate.length; i += 2) + inParameters.push(new ParameterInfo(toIterate[i], toIterate[i + 1])); + toIterate = resMap.get('output'); + let parameters: Map> = new Map(); + parameters.set('input', inParameters); + parameters.set('output', outParameters); + for (let i = 0; i < toIterate.length; i += 2) + outParameters.push(new ParameterInfo(toIterate[i], toIterate[i + 1])); + if (oracle_Data.length > 0) { + oracle_Data = oracle_Data.trim().replace(" ", "_"); + oracle_Data = oracle_Data + .replace("(", " ") + .replace(").", " ") + .trim(); + let splitResult = oracle_Data.split(" "); + if (!controlFlowInfo.oracleInfo.has(splitResult[0])) { + controlFlowInfo.oracleInfo.set( + splitResult[0], + new OracleInfo(splitResult[0]) + ); + } + controlFlowInfo.oracleTaskMap.set(nodeId, splitResult[0]); + let localOracle = controlFlowInfo.oracleInfo.get(splitResult[0]); + localOracle.address = splitResult[1]; + localOracle.functionName = splitResult[2]; + localOracle.functionParameters = parameters.get('input'); + } else controlFlowInfo.localParameters.set(nodeId, parameters); + } + return resMap; +}; + +let getNodeName = (node: any) => + node.name ? node.name.replace(/\s+/g, "_") : node.id; + +export let parseModel = (modelInfo: ModelInfo) => { + return new Promise((resolve, reject) => { + parseBpmn(modelInfo.bpmn) + .then((definitions: any) => { + modelInfo.solidity = "pragma solidity ^0.4.25;\n"; + modelInfo.controlFlowInfoMap = new Map(); + + // Sanity checks + if (!definitions.diagrams || definitions.diagrams.length == 0) + throw new Error("ERROR: No diagram found in BPMN file"); + let proc = definitions.diagrams[0].plane.bpmnElement; + modelInfo.name = proc.name ? proc.name.replace(/\s+/g, "_") : proc.id; + modelInfo.id = proc.id; + if (proc.$type !== "bpmn:Process") { + if (proc.$type === "bpmn:Collaboration") { + for (let i = 0; i < definitions.rootElements.length; i++) + if (definitions.rootElements[i].$type === "bpmn:Process") { + proc = definitions.rootElements[i]; + modelInfo.name = proc.name ? proc.name.replace(/\s+/g, "_") : proc.id; + modelInfo.id = proc.id; + break; + } + } else { + throw new Error("ERROR: No root process model found"); + } + } + + // BPMN to Solidity parsing + + let globalNodeMap: Map = new Map(), + globalNodeIndexMap: Map = new Map(), + globalEdgeIndexMap: Map = new Map(), + globalControlFlowInfo: Array = []; + + //////////////////////////////////////////////////////////// + + globalNodeMap.set(proc.id, proc); + let mainControlFlowInfo = collectControlFlowInfo(proc, globalNodeMap, globalControlFlowInfo); + let globalControlFlowInfoMap: Map = new Map(); + globalControlFlowInfo.forEach(controlFlowInfo => + globalControlFlowInfoMap.set(controlFlowInfo.self.id, controlFlowInfo) + ); + + // Event sub-processes appear in the source list, and not in the nodeList + // In addition, all the elements of a non interrupting subprocess event appears embedded on its parent process + for (let controlFlowInfo of globalControlFlowInfo) { + let indexesToRemove = []; + controlFlowInfo.sources.forEach(nodeId => { + if (globalNodeMap.get(nodeId).triggeredByEvent) { + controlFlowInfo.nodeList.push(nodeId); + indexesToRemove.push(controlFlowInfo.sources.indexOf(nodeId)); + let nodeInfo = globalControlFlowInfoMap.get(nodeId); + if (!globalNodeMap.get(nodeInfo.sources[0]).isInterrupting) + nodeInfo.nodeList.forEach(childId => { + let index = controlFlowInfo.nodeList.indexOf(childId); + if (index >= 0) controlFlowInfo.nodeList.splice(index, 1); + }); + } + }); + indexesToRemove.sort((ind1, ind2) => { + return ind2 - ind1; + }); + indexesToRemove.forEach(index => { + controlFlowInfo.sources.splice(index, 1); + }); + if (is(globalNodeMap.get(controlFlowInfo.self.id), "bpmn:SubProcess") && + controlFlowInfo.self.triggeredByEvent && + globalNodeMap.get(controlFlowInfo.sources[0]).isInterrupting == false) { + controlFlowInfo.isEmbedded = false; + } + } + + let hasExternalCall = nodeId => { + let node = globalNodeMap.get(nodeId); + return is(node, "bpmn:ServiceTask"); + }; + + modelInfo.globalNodeMap = globalNodeMap; + + for (let controlFlowInfo of globalControlFlowInfo) { + if (!controlFlowInfo.isEmbedded) { + let multiinstanceActivities = [], + callActivities = [], + nonInterruptingEvents = [], + catchingMessages = []; + + controlFlowInfo.nodeList + .map(nodeId => globalNodeMap.get(nodeId)) + .forEach(e => { + if ((is(e, "bpmn:Task") || is(e, "bpmn:SubProcess")) && e.loopCharacteristics && + e.loopCharacteristics.$type === "bpmn:MultiInstanceLoopCharacteristics") { + controlFlowInfo.multiinstanceActivities.set(e.id, getNodeName(e)); + multiinstanceActivities.push(e.id); + } else if (is(e, "bpmn:CallActivity")) { + controlFlowInfo.callActivities.set(e.id, getNodeName(e)); + callActivities.push(e.id); + } else if (is(e, "bpmn:IntermediateCatchEvent") && is(e.eventDefinitions[0], "bpmn:MessageEventDefinition")) + catchingMessages.push(e.id); + else if (is(e, "bpmn:StartEvent") && is(e.eventDefinitions[0], "bpmn:MessageEventDefinition")) + catchingMessages.push(e.id); + }); + + // It is also necessary to add boundary events of embedded sub-processes + + controlFlowInfo.sources.forEach(nodeId => { + let start = globalNodeMap.get(nodeId); + if (start.eventDefinitions && start.eventDefinitions[0] && is(start.eventDefinitions[0], "bpmn:MessageEventDefinition") && + controlFlowInfo.nodeList.indexOf(nodeId) < 0) { + controlFlowInfo.nodeList.push(nodeId); + if (catchingMessages.indexOf(nodeId) < 0) + catchingMessages.push(nodeId); + } + }); + + controlFlowInfo.boundaryEvents.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.outgoing) + for (let outgoing of node.outgoing) + controlFlowInfo.edgeList.push(outgoing.id); + if (!node.cancelActivity) { + controlFlowInfo.nonInterruptingEvents.set(node.id, getNodeName(node)); + nonInterruptingEvents.push(node.id); + controlFlowInfo.nodeList.push(nodeId); // Eager reinsertion + if (node.eventDefinitions[0] && is(node.eventDefinitions[0], 'bpmn:MessageEventDefinition')) { + if (catchingMessages.indexOf(nodeId) < 0) + catchingMessages.push(nodeId); + } + } else if (node.eventDefinitions && is(node.eventDefinitions[0], "bpmn:MessageEventDefinition")) { + if (controlFlowInfo.nodeList.indexOf(nodeId) < 0) + controlFlowInfo.nodeList.push(nodeId); + if (catchingMessages.indexOf(nodeId) < 0) + catchingMessages.push(nodeId); + } + }); + + globalNodeMap.forEach(node => { + if (is(node, "bpmn:SubProcess") && node.triggeredByEvent && controlFlowInfo.nodeList.indexOf(node.id)) { + for (let start of node.flowElements.filter(e => is(e, "bpmn:FlowNode") && is(e, "bpmn:StartEvent"))) { + if (start.isInterrupting == false) { + let parent = globalNodeMap.get(start.$parent.id); + controlFlowInfo.nonInterruptingEvents.set(start.id, getNodeName(parent)); + nonInterruptingEvents.push(start.id); + controlFlowInfo.nodeList.push(start.id); + if (start.eventDefinitions[0] && is(start.eventDefinitions[0], "bpmn:MessageEventDefinition")) { + if (catchingMessages.indexOf(start.id) < 0) + catchingMessages.push(start.id); + } + } + if (controlFlowInfo.boundaryEvents.indexOf(start.id) < 0) { + controlFlowInfo.boundaryEvents.push(start.id); + if (controlFlowInfo.nodeList.indexOf(start.$parent.id) < 0) + controlFlowInfo.nodeList.push(start.$parent.id); + } + if (start.eventDefinitions[0] && is(start.eventDefinitions[0], "bpmn:MessageEventDefinition")) { + if (controlFlowInfo.nodeList.indexOf(start.id) < 0) + controlFlowInfo.nodeList.push(start.id); + if (catchingMessages.indexOf(start.id) < 0) + catchingMessages.push(start.id); + } + if (start.outgoing) + for (let outgoing of start.outgoing) + controlFlowInfo.edgeList.push(outgoing.id); + } + } + }); + + let part1: Array = []; + let part2: Array = []; + controlFlowInfo.nodeList.forEach(nodeId => { + if (hasExternalCall(nodeId)) part1.push(nodeId); + else part2.push(nodeId); + }); + controlFlowInfo.nodeList = part1.concat(part2); + controlFlowInfo.nodeList.forEach( + (nodeId: string, index: number) => { + let node = globalNodeMap.get(nodeId); + controlFlowInfo.nodeIndexMap.set(nodeId, index + 1); + globalNodeIndexMap.set(nodeId, index + 1); + controlFlowInfo.nodeNameMap.set(nodeId, getNodeName(globalNodeMap.get(nodeId))); + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0) { + if (is(node, 'bpmn:CallActivity')) + controlFlowInfo.externalBundles.set(nodeId, node.documentation[0].text); + else + extractParameters(node.documentation[0].text, node.id, controlFlowInfo); + } + } + ); + controlFlowInfo.edgeList.forEach( + (edgeId: string, index: number) => { + controlFlowInfo.edgeIndexMap.set(edgeId, index + 1); + globalEdgeIndexMap.set(edgeId, index + 1); + } + ); + controlFlowInfo.catchingMessages = catchingMessages; + + // ControlFlow Perspective: Generation of Smart Contracts + let codeGenerationInfo = { + nodeList: controlFlowInfo.nodeList, + nodeMap: globalNodeMap, + catchingMessages: controlFlowInfo.catchingMessages, + multiinstanceActivities: multiinstanceActivities, + callActivities: callActivities, + nonInterruptingEvents: nonInterruptingEvents, + oracleInfo: controlFlowInfo.oracleInfo, + oracleTaskMap: controlFlowInfo.oracleTaskMap, + processId: () => controlFlowInfo.self.id, + nodeName: nodeId => getNodeName(globalNodeMap.get(nodeId)), + eventType: nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.eventDefinitions && node.eventDefinitions[0]) { + let cad = node.eventDefinitions[0].$type; + return cad.substring(5, cad.length - 15); + } + return "Default"; + }, + allEventTypes: () => { + let taken = []; + globalNodeMap.forEach(node => { + if (node.eventDefinitions && node.eventDefinitions[0] && !is(node.eventDefinitions[0], "bpmn:TerminateEventDefinition") && !is(node.eventDefinitions[0], "bpmn:MessageEventDefinition")) { + let cad = node.eventDefinitions[0].$type; + if (taken.indexOf(cad.substring(5, cad.length - 15)) < 0) + taken.push(cad.substring(5, cad.length - 15)); + } + }); + return taken; + }, + getMessages: () => { + let taken = []; + let candidates = controlFlowInfo.boundaryEvents; + controlFlowInfo.nodeList.forEach(nodeId => { + if (is(globalNodeMap.get(nodeId), "bpmn:SubProcess")) { + let subP = globalControlFlowInfoMap.get(nodeId); + candidates = candidates.concat(subP.boundaryEvents); + subP.sources.forEach(id => { + if (!is(globalNodeMap.get(id), "bpmn:Subprocess") && candidates.indexOf(id) < 0) + candidates.push(id); + }); + } + }); + candidates.forEach(evtId => { + let evt = globalNodeMap.get(evtId); + if (evt.eventDefinitions && evt.eventDefinitions[0] && is(evt.eventDefinitions[0], "bpmn:MessageEventDefinition")) + taken.push(evtId); + }); + return taken; + }, + getThrowingMessages: () => { + let res = []; + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if ((is(node, "bpmn:EndEvent") || is(node, "bpmn:IntermediateThrowEvent")) && + node.eventDefinitions && node.eventDefinitions[0] && is(node.eventDefinitions[0], "bpmn:MessageEventDefinition")) + res.push(nodeId); + }); + return res; + }, + getThrowingEvents: (subprocId, evType) => { + let res = []; + globalNodeMap.forEach(node => { + if (node.eventDefinitions && node.eventDefinitions[0]) { + let cad = node.eventDefinitions[0].$type; + if (cad.substring(5, cad.length - 15) === evType) { + if ((is(node, "bpmn:EndEvent") || is(node, "bpmn:IntermediateThrowEvent")) && + (node.$parent.id === subprocId || controlFlowInfo.nodeList.indexOf(node.id) >= 0)) { + res.push(node.id); + } + } + } + }); + return res; + }, + getCatchingEvents: (subprocId) => { + let res = []; + globalNodeMap.forEach(node => { + if (node.eventDefinitions && node.eventDefinitions[0]) { + if (is(node, "bpmn:StartEvent")) { + let parent = globalNodeMap.get(node.$parent.id); + if (parent.triggeredByEvent && parent.$parent.id === subprocId) + res.unshift(node.id); + else if (!parent.triggeredByEvent && (parent.id === subprocId || controlFlowInfo.nodeList.indexOf(parent.id) > -1)) + res.push(node.id); + } else if (is(node, "bpmn:BoundaryEvent") || is(node, "bpmn:IntermediateCatchEvent")) { + if (node.$parent.id === subprocId || controlFlowInfo.nodeList.indexOf(node.$parent.id) > -1) + res.push(node.id); + } + } + }); + return res; + }, + getTerminateCandidates: (subprocId) => { + let res = []; + globalNodeMap.forEach(node => { + if (node.eventDefinitions && node.eventDefinitions[0]) { + if (is(node, "bpmn:BoundaryEvent") && node.cancelActivity == false) { + if(globalControlFlowInfoMap.has(node.id)) { + let localC = globalControlFlowInfoMap.get(node.id); + localC.nodeList.forEach(elemId => { + let elem = globalNodeMap.get(elemId); + if(elem.eventDefinitions && is(elem.eventDefinitions[0], "bpmn:TerminateEventDefinition") && elem.$parent.id === node.$parent.id) + res.push(node.id); + }) + + } else { + console.log('Missing Non Interrupting event'); + } + } + } + }); + return res; + }, + getProcessCandidatesMaskFrom: (evtId, evtType, evtCode, sourceProcesses, allEvents) => { + let eventList = []; + let bitarray = []; + allEvents.forEach(nodeId => { + let cad = globalNodeMap.get(nodeId).eventDefinitions[0].$type; + if (evtType === cad.substring(5, cad.length - 15) && evtCode === getNodeName(globalNodeMap.get(nodeId))) + eventList.push(nodeId); + }); + sourceProcesses.forEach(procId => { + let parent = globalNodeMap.get(procId); + let previousParent = parent; + let res = []; + let eventFound = false; + while (!eventFound && res.length == 0 && parent.$parent && controlFlowInfo.self.id !== parent.id) { + parent = globalNodeMap.get(parent.$parent.id); + eventList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (!eventFound && is(node, "bpmn:BoundaryEvent") && node.attachedToRef.id === previousParent.id) { + eventFound = node.cancelActivity != false; + if (eventFound) res = [nodeId]; + else res.push(nodeId); + } + }); + if (res.length == 0) { + eventList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (!eventFound && is(node, "bpmn:StartEvent") && node.$parent.triggeredByEvent && node.$parent.$parent.id === parent.id) { + eventFound = node.isInterrupting != false; + if (eventFound) res = [nodeId]; + else res.push(nodeId); + } + }) + } + previousParent = parent; + } + if (res.indexOf(evtId)) + bitarray[globalNodeIndexMap.get(procId)] = 1; + }); + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return result === "0b" ? 0 : new BigNumber(result).toFixed(); + }, + getCatchingEventsFrom: (procId, evtType, evtCode) => { + // Escalation and Error catching events. + // No intermediate events in normal flow allowed + let res = []; + let parent = globalNodeMap.get(procId); + let eventFound = false; + let candidates = controlFlowInfo.boundaryEvents.concat(controlFlowInfo.nodeList); + let eventList = []; + candidates.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.eventDefinitions) { + let cad = node.eventDefinitions[0].$type; + let type = cad.substring(5, cad.length - 15); + if (type === evtType && evtCode === getNodeName(globalNodeMap.get(nodeId)) && eventList.indexOf(nodeId) < 0) { + eventList.push(nodeId); + } + } + }); + if (!parent.triggeredByEvent) { + eventList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (!eventFound && is(node, "bpmn:StartEvent") && node.$parent.triggeredByEvent && node.$parent.$parent.id === parent.id) { + eventFound = node.isInterrupting != false; + if (eventFound) res = [nodeId]; + else res.push(nodeId); + } + }); + } + if (controlFlowInfo.self.id === procId || res.length > 0) { + return res; + } else { + if (parent.triggeredByEvent) + parent = globalNodeMap.get(parent.$parent.id); + let previousParent = parent; + while (!eventFound && res.length == 0 && parent.$parent && controlFlowInfo.self.id !== parent.id) { + parent = globalNodeMap.get(parent.$parent.id); + eventList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (!eventFound && is(node, "bpmn:BoundaryEvent") && node.attachedToRef.id === previousParent.id) { + eventFound = node.cancelActivity != false; + if (eventFound) res = [nodeId]; + else res.push(nodeId); + } + }); + if (res.length == 0) { + eventList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (!eventFound && is(node, "bpmn:StartEvent") && node.$parent.triggeredByEvent && node.$parent.$parent.id === parent.id) { + eventFound = node.isInterrupting != false; + if (eventFound) res = [nodeId]; + else res.push(nodeId); + } + }) + } + previousParent = parent; + } + return res; + } + }, + getWorkItemsGroupByParameters: (isInput) => { + let name2Ids: Map = new Map(); + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, 'bpmn:UserTask') || is(node, 'bpmn:ReceiveTask') || catchingMessages.indexOf(nodeId) >= 0) { + let params = ""; + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0 && extractParameters(node.documentation[0].text, nodeId, null) !== undefined) { + let localParams = isInput + ? extractParameters(node.documentation[0].text, nodeId, null).get("input") + : extractParameters(node.documentation[0].text, nodeId, null).get("output"); + if (localParams.length > 0) { + params = localParams[0]; + for (let i = 2; i < localParams.length; i += 2) params += localParams[i]; + } + } + let name = getNodeName(globalNodeMap.get(nodeId)) + params; + if (!name2Ids.has(name)) { + name2Ids.set(name, []); + } + name2Ids.get(name).push(nodeId); + } + }); + return name2Ids; + }, + getContracts2Call: () => { + let res = callActivities.concat(multiinstanceActivities); + nonInterruptingEvents.forEach(evtId => { + let node = globalNodeMap.get(evtId); + res.push(is(node, "bpmn:StartEvent") ? node.$parent.id : evtId); + }); + return res; + }, + getContracts2CallFrom: (subprocId, candidates) => { + let res = [subprocId]; + if (!controlFlowInfo.callActivities.has(subprocId)) { + candidates.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + while (node.$parent) { + if (node.$parent.id === subprocId) { + res.push(nodeId); + break; + } + node = node.$parent; + } + }); + } + return res; + }, + getContracts2CallMaskFrom: (subprocId, candidates) => { + let bitarray = []; + candidates.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + while (node.$parent) { + if (node.$parent.id === subprocId) { + bitarray[globalNodeIndexMap.get(nodeId)] = 1; + break; + } + node = node.$parent; + } + }); + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return result === "0b" ? 0 : new BigNumber(result).toFixed(); + }, + getContracts2CallArray: (subprocId, candidates) => { + let res = '[uint(' + globalNodeIndexMap.get(candidates[0]) + ')'; + for (let i = 1; i < candidates.length; i++) + res += ', uint(' + globalNodeIndexMap.get(candidates[i]) + ')'; + return res + ']'; + }, + getPossibleKillSubprocess: () => { + let res = []; + controlFlowInfo.boundaryEvents.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.$parent.triggeredByEvent && node.$parent.$parent.id !== controlFlowInfo.self.id) { + if (node.isInterrupting != false && res.indexOf(node.$parent.$parent.id) < 0) + res.push(node.$parent.$parent.id); + } else if (node.attachedToRef) { + let attachedTo = node.attachedToRef.id; + if (node.cancelActivity != false && res.indexOf(attachedTo) < 0) { + res.push(attachedTo); + } + } + }); + globalNodeMap.forEach(node => { + if (node.eventDefinitions && node.eventDefinitions[0]) { + if (is(node, "bpmn:BoundaryEvent") && node.cancelActivity == false) { + if(globalControlFlowInfoMap.has(node.id)) { + let localC = globalControlFlowInfoMap.get(node.id); + localC.nodeList.forEach(elemId => { + let elem = globalNodeMap.get(elemId); + if(elem.eventDefinitions && is(elem.eventDefinitions[0], "bpmn:TerminateEventDefinition") && elem.$parent.id === node.$parent.id && controlFlowInfo.nodeList.indexOf(node.$parent.id) >= 0 && res.indexOf(node.$parent.id) < 0 && node.$parent.id != controlFlowInfo.self.id) { + res.push(node.$parent.id); + } + }) + } + } + } + }); + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.eventDefinitions && is(node.eventDefinitions[0], "bpmn:TerminateEventDefinition")) { + if(res.indexOf(node.$parent.id) < 0 && node.$parent.id != controlFlowInfo.self.id && !is(globalNodeMap.get(controlFlowInfo.self.id), "bpmn:BoundaryEvent")) { + console.log('I am here 2'); + res.push(node.$parent.id); + } + } + }); + return res; + }, + getCountExternalTasks: () => { + let res = 0; + controlFlowInfo.nodeList.forEach(nodeId => { + if (hasExternalCall(nodeId)) res++; + }); + return res; + }, + getStartedMessages: processId => { + let res = []; + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, "bpmn:StartEvent") && node.$parent.id === processId && node.eventDefinitions + && is(node.eventDefinitions[0], "bpmn:MessageEventDefinition") && globalNodeMap.get(node.$parent.id).triggeredByEvent) + res.push(nodeId); + }); + return res; + }, + getParent: nodeId => { + // Retrieves the id of the parent + let node = globalNodeMap.get(nodeId); + if (is(node, "bpmn:StartEvent") && node.$parent && globalNodeMap.get(node.$parent.id).triggeredByEvent) + return globalNodeMap.get(node.$parent.id).$parent.id; + if (is(node, "bpmn:BoundaryEvent") && node.cancelActivity) + return node.attachedToRef.id; + return node.$parent ? node.$parent.id : nodeId; + }, + getContractName: nodeId => { + // Retrieves the contract name related to the node. + let node = globalNodeMap.get(nodeId); + if (is(node, "bpmn:StartEvent") && node.$parent && globalNodeMap.get(node.$parent.id).triggeredByEvent) + return node.$parent.id; + if (is(node, "bpmn:BoundaryEvent")) return node.id; + return controlFlowInfo.self.id; + }, + getAllChildren: (subprocId, direct) => { + let taken = direct ? [] : [subprocId]; + controlFlowInfo.nodeList + .map(nodeId => globalNodeMap.get(nodeId)) + .forEach(e => { + if (is(e, "bpmn:SubProcess") || callActivities.indexOf(e.id) >= 0 || (nonInterruptingEvents.indexOf(e.id) >= 0 && !is(e, "bpmn:StartEvent"))) + if (((direct && subprocId !== e.id && e.$parent.id === subprocId) || !direct) && taken.indexOf(e.id) < 0) + taken.push(e.id); + }); + return taken; + }, + isStartingContractEvent: (eventId, processId) => { + let evt = globalNodeMap.get(eventId); + if (is(evt, "bpmn:StartEvent")) { + if (globalNodeMap.get(evt.$parent.id).triggeredByEvent) + return evt.$parent.id !== processId; + if (is(evt.eventDefinitions[0], "bpmn:MessageEventDefinition")) + return true; + } else if (is(evt, "bpmn:BoundaryEvent")) { + return eventId !== processId; + } else if (is(evt, "bpmn:IntermediateCatchEvent") && is(evt.eventDefinitions[0], "bpmn:MessageEventDefinition")) + return true; + return false; + }, + isInterrupting: eventId => { + // True if an event is interrupting + let node = globalNodeMap.get(eventId); + if (node.eventDefinitions && is(node.eventDefinitions[0], "bpmn:ErrorEventDefinition")) + return true; + if (is(node, "bpmn:StartEvent") && node.$parent && globalNodeMap.get(node.$parent.id).triggeredByEvent) + return node.isInterrupting != false; + if (is(node, "bpmn:BoundaryEvent")) + return node.cancelActivity != false; + return false; + }, + isEmbeddedSubprocess: subprocessId => { + return globalControlFlowInfoMap.get(subprocessId).isEmbedded; + }, + isBoundaryEvent: evtId => { + return controlFlowInfo.boundaryEvents.indexOf(evtId) >= 0; + }, + preMarking: nodeId => { + let node = globalNodeMap.get(nodeId); + let bitarray = []; + if (node.incoming) + for (let incoming of node.incoming) + bitarray[controlFlowInfo.edgeIndexMap.get(incoming.id)] = 1; + else bitarray[0] = 1; + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + postMarking: nodeId => { + let node = globalNodeMap.get(nodeId); + let bitarray = []; + let result = "0b"; + if (node.outgoing) + for (let outgoing of node.outgoing) { + bitarray[controlFlowInfo.edgeIndexMap.get(outgoing.id)] = 1; + } + else result = "0"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + subprocessNodeMarking: subprocessId => { + let bitarray = []; + globalNodeMap.forEach(node => { + if (node.$parent && node.$parent.id === subprocessId) { + if (is(node, "bpmn:Task") || is(node, 'bpmn:SubProcess')) + bitarray[globalNodeIndexMap.get(node.id)] = 1; + else if (!globalNodeMap.get(subprocessId).triggeredByEvent && node.eventDefinitions && node.eventDefinitions[0] && + is(node.eventDefinitions[0], "bpmn:MessageEventDefinition")) + bitarray[globalNodeIndexMap.get(node.id)] = 1; + } + }); + let result = bitarray.length > 0 ? "0b" : 0; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + subprocessNodeFullMarking: subprocId => { + let children = [subprocId]; + let bitarray = []; + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, "bpmn:SubProcess") || callActivities.indexOf(node.id) >= 0 || (nonInterruptingEvents.indexOf(node.id) >= 0 && !is(node, "bpmn:StartEvent"))) { + while (node.$parent) { + if (node.$parent.id === subprocId) { + if (multiinstanceActivities.indexOf(nodeId) >= 0 || callActivities.indexOf(node.id) >= 0 || nonInterruptingEvents.indexOf(node.id) >= 0) { + bitarray[globalNodeIndexMap.get(nodeId)] = 1; + } + else if (children.indexOf(nodeId) < 0) { + children.push(nodeId); + } + break; + } + node = node.$parent; + } + } + }); + let result = "0b"; + if (globalNodeIndexMap.get(subprocId)) + bitarray[globalNodeIndexMap.get(subprocId)] = 1; + controlFlowInfo.nodeList + .map(nodeId => globalNodeMap.get(nodeId)) + .forEach(node => { + if (node.$parent && children.indexOf(node.$parent.id) >= 0) { + bitarray[globalNodeIndexMap.get(node.id)] = 1; + } + }); + catchingMessages + .map(evtId => globalNodeMap.get(evtId)) + .forEach(evt => { + if (evt.attachedToRef && children.indexOf(evt.attachedToRef) >= 0) { + bitarray[globalNodeIndexMap.get(evt.id)] = 1; + } + }); + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return result === '0b' ? new BigNumber(0) : new BigNumber(result).toFixed(); + }, + subprocessStartMarking: subprocessId => { + let toSearch = globalNodeMap.get(subprocessId); + let bitarray = []; + let result = "0b"; + if (is(toSearch, "bpmn:BoundaryEvent")) { + for (let outgoing of toSearch.outgoing) + bitarray[controlFlowInfo.edgeIndexMap.get(outgoing.id)] = 1; + } else { + for (let node of toSearch.flowElements.filter( + e => is(e, "bpmn:FlowNode") && is(e, "bpmn:StartEvent") + )) { + if (node.$parent.id === subprocessId) + if (!globalNodeMap.get(node.$parent.id).triggeredByEvent && + node.eventDefinitions && node.eventDefinitions[0] && + is(node.eventDefinitions[0], "bpmn:MessageEventDefinition")) + bitarray[0] = 1; + else if (node.outgoing) + for (let outgoing of node.outgoing) + bitarray[controlFlowInfo.edgeIndexMap.get(outgoing.id)] = 1; + } + } + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + getAllAncestorsMask: subprocId => { + let bitarray = []; + let result = "0b"; + let node = globalNodeMap.get(subprocId); + while (node.$parent) { + bitarray[controlFlowInfo.nodeIndexMap.get(node.id)] = 1; + node = node.$parent; + } + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + subprocessMarking: subprocessId => { + let bitarray = []; + let result = "0b"; + let localInfo = globalControlFlowInfoMap.get(subprocessId); + let edgeList = []; + localInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.$parent && node.$parent.id === subprocessId && node.incoming) { + for (let incoming of node.incoming) { + edgeList.push(incoming.id); + } + } + }); + edgeList.forEach(edgeId => { + bitarray[controlFlowInfo.edgeIndexMap.get(edgeId)] = 1; + }); + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + subprocessFullMarking: subprocId => { + let bitarray = []; + let result = "0b"; + let children = [subprocId]; + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, "bpmn:SubProcess") && multiinstanceActivities.indexOf(nodeId) < 0) { + while (node.$parent) { + if (node.$parent.id === subprocId) { + if (children.indexOf(nodeId) < 0) + children.push(nodeId); + break; + } + node = node.$parent; + } + } + }); + children.forEach(subprocessId => { + let localInfo = globalControlFlowInfoMap.get(subprocessId); + localInfo.edgeList.forEach(edgeId => { + bitarray[controlFlowInfo.edgeIndexMap.get(edgeId)] = 1; + }); + }); + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + flowEdgeIndex: flowEdgeId => { + let bitarray = []; + bitarray[controlFlowInfo.edgeIndexMap.get(flowEdgeId)] = 1; + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + flowNodeIndex: flowNodeId => { + let bitarray = []; + bitarray[globalNodeIndexMap.get(flowNodeId)] = 1; + let result = "0b"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + nodeRealIndex: nodeId => { + return globalNodeIndexMap.get(nodeId); + }, + isPartOfDeferredChoice: eventId => { + let event = globalNodeMap.get(eventId); + if (event.incoming) { + let node = event.incoming[0].sourceRef; + return is(node, "bpmn:EventBasedGateway"); + } + return false; + }, + getDeferredChoiceElements: nodeId => { + let event = globalNodeMap.get(nodeId); + let res = []; + if (event.incoming) { + let node = event.incoming[0].sourceRef; + if (is(node, "bpmn:EventBasedGateway")) { + for (let outgoing of node.outgoing) { + if (outgoing.targetRef.id !== nodeId) + res.push(outgoing.targetRef.id); + } + } + } + return res; + }, + deferredChoiceNodeMarking: nodeId => { + let event = globalNodeMap.get(nodeId); + let bitarray = []; + if (event.incoming) { + let node = event.incoming[0].sourceRef; + if (is(node, "bpmn:EventBasedGateway")) { + for (let outgoing of node.outgoing) { + bitarray[controlFlowInfo.nodeIndexMap.get(outgoing.targetRef.id)] = 1; + } + } + } + let result = "0"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + deferredChoiceMarking: eventId => { + let event = globalNodeMap.get(eventId); + let node = event.incoming[0].sourceRef; + let bitarray = []; + let result = "0b"; + if (node.outgoing) + for (let outgoing of node.outgoing) { + bitarray[controlFlowInfo.edgeIndexMap.get(outgoing.id)] = 1; + } + else result = "0"; + for (let i = bitarray.length - 1; i >= 0; i--) + result += bitarray[i] ? "1" : "0"; + return new BigNumber(result).toFixed(); + }, + globalDeclarations: () => { + if (controlFlowInfo.globalParameters.length > 0) + return controlFlowInfo.globalParameters; + else return ""; + }, + getOracleFunction: nodeId => { + if (controlFlowInfo.oracleTaskMap.has(nodeId)) + return controlFlowInfo.oracleInfo.get( + controlFlowInfo.oracleTaskMap.get(nodeId) + ).functionName; + return ""; + }, + nodeParameters: nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0) { + let resDict = extractParameters(node.documentation[0].text, nodeId, null); + return resDict !== undefined ? resDict.get("input").length > 0 || resDict.get("output").length > 0 : false; + } + return false; + }, + typeParameters: (nodeId, isInput, hasPreviousParameter) => { + let node = globalNodeMap.get(nodeId); + let res = ""; + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0 && extractParameters(node.documentation[0].text, nodeId, null) !== undefined) { + let localParams = isInput + ? extractParameters(node.documentation[0].text, nodeId, null).get("input") + : extractParameters(node.documentation[0].text, nodeId, null).get("output"); + if (localParams.length > 0) { + res = localParams[0]; + for (let i = 2; i < localParams.length; i += 2) + res += ", " + localParams[i]; + } + } + return hasPreviousParameter && res.length > 0 + ? ", " + res + : res; + }, + concatParameters: (nodeId, isInput, hasType, hasPreviousParameter) => { + let node = globalNodeMap.get(nodeId); + let res = ""; + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0 && extractParameters(node.documentation[0].text, nodeId, null) !== undefined) { + let localParams = isInput + ? extractParameters(node.documentation[0].text, nodeId, null).get("input") + : extractParameters(node.documentation[0].text, nodeId, null).get("output"); + if (localParams.length > 0) { + res = hasType + ? localParams[0] + " " + localParams[1] + : localParams[1]; + for (let i = 2; i < localParams.length; i += 2) + res += "," + (hasType + ? localParams[i] + " " + localParams[i + 1] + : localParams[i + 1]); + } + } + return hasPreviousParameter && res.length > 0 ? ", " + res : res; + }, + nodeFunctionBody: nodeId => { + let node = globalNodeMap.get(nodeId); + if (node.script) { + return node.script.split("->"); + } else if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0 && extractParameters(node.documentation[0].text, nodeId, null) !== undefined) { + return extractParameters(node.documentation[0].text, nodeId, null).get("body"); + } else return ""; + }, + getCondition: flowEdge => + flowEdge.conditionExpression + ? flowEdge.conditionExpression.body + : flowEdge.name ? flowEdge.name : flowEdge.id, + is: is + }; + + let localSolidity = bpmn2solTemplate(codeGenerationInfo); + + // Code for using the WorkList template + let userTaskList = []; + let parameterInfo: Map>> = new Map(); + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, 'bpmn:UserTask') || is(node, 'bpmn:ReceiveTask')) { + userTaskList.push(nodeId); + if (controlFlowInfo.localParameters.has(nodeId) && (controlFlowInfo.localParameters.get(nodeId).get('input').length > 0 || controlFlowInfo.localParameters.get(nodeId).get('output').length > 0)) { + parameterInfo.set(nodeId, controlFlowInfo.localParameters.get(nodeId)); + } + } + }); + if (controlFlowInfo.catchingMessages.length > 0) + userTaskList = userTaskList.concat(controlFlowInfo.catchingMessages); + + // WorkList: Smart Contract Generation + let workListGenerationInfo = { + nodeList: userTaskList, + restrictRelation: restrictRelation, + parameterInfo: parameterInfo, + nodeIndex: globalNodeIndexMap, + nodeMap: globalNodeMap, + processId: () => controlFlowInfo.self.id, + nodeName: nodeId => { + return getNodeName(globalNodeMap.get(nodeId)); + }, + getParameterType: (nodeId, isInput, isType, hasPrevious) => { + let res = ""; + if (parameterInfo.get(nodeId)) { + let localParams = isInput + ? parameterInfo.get(nodeId).get("input") + : parameterInfo.get(nodeId).get("output"); + if (localParams && localParams.length > 0) { + res = isType ? localParams[0].type : localParams[0].name; + for (let i = 1; i < localParams.length; i++) + res += isType + ? ", " + localParams[i].type + : ", " + localParams[i].name; + } + } + return res.length > 0 && hasPrevious ? ", " + res : res; + }, + getParameters: (nodeId, isInput, hasType, hasPrevious) => { + let res = ""; + if (parameterInfo.get(nodeId)) { + let localParams = isInput + ? parameterInfo.get(nodeId).get("input") + : parameterInfo.get(nodeId).get("output"); + if (localParams && localParams.length > 0) { + res = hasType + ? localParams[0].type + " " + localParams[0].name + : localParams[0].name; + for (let i = 1; i < localParams.length; i++) + res += hasType + ? ", " + localParams[i].type + " " + localParams[i].name + : ", " + localParams[i].name; + } + } + return res.length > 0 && hasPrevious ? ", " + res : res; + }, + getWorkItemsGroupByParameters: (isInput) => { + let name2Ids: Map = new Map(); + controlFlowInfo.nodeList.forEach(nodeId => { + let node = globalNodeMap.get(nodeId); + if (is(node, 'bpmn:UserTask') || is(node, 'bpmn:ReceiveTask') || catchingMessages.indexOf(nodeId) >= 0) { + let params = ""; + if (node.documentation && node.documentation[0].text && node.documentation[0].text.length > 0 && extractParameters(node.documentation[0].text, nodeId, null) !== undefined) { + let localParams = isInput + ? extractParameters(node.documentation[0].text, nodeId, null).get("input") + : extractParameters(node.documentation[0].text, nodeId, null).get("output"); + if (localParams.length > 0) { + params = localParams[0]; + for (let i = 2; i < localParams.length; i += 2) params += localParams[i]; + } + } + let name = getNodeName(globalNodeMap.get(nodeId)) + params; + if (!name2Ids.has(name)) { + name2Ids.set(name, []); + } + name2Ids.get(name).push(nodeId); + } + }); + return name2Ids; + }, + is: is + }; + modelInfo.solidity += localSolidity; + if (userTaskList.length > 0) { + modelInfo.solidity += workList2solTemplate(workListGenerationInfo); + } + modelInfo.controlFlowInfoMap.set(controlFlowInfo.self.id, controlFlowInfo); + } else { + controlFlowInfo.nodeList.forEach(nodeId => + controlFlowInfo.nodeIndexMap.set(nodeId, globalNodeIndexMap.get(nodeId)) + ); + controlFlowInfo.edgeList.forEach(edgeId => + controlFlowInfo.edgeIndexMap.set(edgeId, globalEdgeIndexMap.get(edgeId)) + ); + } + } + + ////////////////////////////////////////////////////////////////////////////////// + + modelInfo.entryContractName = modelInfo.name + ":" + (proc.name ? proc.name.replace(/\s+/g, "_") : proc.id) + "_Contract"; + + resolve(); + }) + .catch(err => { + throw new Error(err); + reject(); + }); + }); +}; diff --git a/v3.0/caterpillar-core/src/models/models.store.ts b/v3.0/caterpillar-core/src/models/models.store.ts new file mode 100644 index 0000000..91fa9cd --- /dev/null +++ b/v3.0/caterpillar-core/src/models/models.store.ts @@ -0,0 +1,3 @@ +import { ModelInfo } from "./definitions"; + +export let modelStore: Map = new Map(); diff --git a/v3.0/caterpillar-core/src/repo/procModelData.ts b/v3.0/caterpillar-core/src/repo/procModelData.ts new file mode 100644 index 0000000..00f349c --- /dev/null +++ b/v3.0/caterpillar-core/src/repo/procModelData.ts @@ -0,0 +1,44 @@ +import * as mongoose from 'mongoose'; + +export let repoSchema = mongoose.model('ProcessRepo', { + rootProcessID: String, + rootProcessName: String, + bpmnModel: String, + solidityCode: String, + abi: String, + bytecode: String, + indexToElement: [mongoose.Schema.Types.Mixed], + worklistAbi: String, +}); + +export let registrySchema = mongoose.model('RegistryRepo', { + address: String, + solidityCode: String, + abi: String, + bytecode: String, +}); + +export let policySchema = mongoose.model('PolicyRepo', { + address: String, + model: String, + solidityCode: String, + abi: String, + bytecode: String, + indexToRole: [String], + accessControlAbi: String, + accessControlBytecode: String, +}); + +export let roleTaskSchema = mongoose.model('RoleTaskRepo', { + address: String, + solidityCode: String, + abi: String, + bytecode: String, +}); + +export let interpreterSchema = mongoose.model('InterpreterRepo', { + procID: String, + procName: String, + bpmnModel: String, + indexToElement: [mongoose.Schema.Types.Mixed], +}); diff --git a/v3.0/caterpillar-core/src/tsconfig.json b/v3.0/caterpillar-core/src/tsconfig.json new file mode 100644 index 0000000..b3538c2 --- /dev/null +++ b/v3.0/caterpillar-core/src/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015", + "outDir": "../out", + "lib": [ + "es2015" + ], + "sourceMap": true, + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "rootDir": "." + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/src/www.ts b/v3.0/caterpillar-core/src/www.ts new file mode 100644 index 0000000..b38ebd4 --- /dev/null +++ b/v3.0/caterpillar-core/src/www.ts @@ -0,0 +1,73 @@ +import app from './app'; +import * as debugModule from 'debug'; +import * as http from 'http'; + +const debug = debugModule('node-express-typescript:server'); + +// Get port from environment and store in Express. +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +// create server and listen on provided port (on all network interfaces). +const server = http.createServer(app); +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ +function normalizePort(val: any): number|string|boolean { + let port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + let bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ +function onListening() { + let addr = server.address(); + let bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + + console.log('Listening on ' + bind); +} diff --git a/v3.0/caterpillar-core/templates/bpmn2sol.ejs b/v3.0/caterpillar-core/templates/bpmn2sol.ejs new file mode 100644 index 0000000..76f2118 --- /dev/null +++ b/v3.0/caterpillar-core/templates/bpmn2sol.ejs @@ -0,0 +1,624 @@ + +import "AbstractFactory"; +import "AbstractProcess"; +import "AbstractRegistry"; +<% if(oracleTaskMap.size > 0) { -%> +contract Oracle_Wrapper { +<% oracleTaskMap.forEach((oracleKey, nodeId, map) => { -%> + function <%= getOracleFunction(nodeId) %> (<%= typeParameters(nodeId, true, false) %>, function (uint<%= typeParameters(nodeId, false, true) %>) external returns(bool)) external returns(uint); +<% }) -%> +} +<% } -%> + +contract <%= nodeName(processId()) %>_Factory is AbstractFactory { + function newInstance(address parent, address processRegistry) public returns(address) { + <%= nodeName(processId()) %>_Contract newContract = new <%= nodeName(processId()) %>_Contract(parent, worklist, processRegistry); + return newContract; + } + + function startInstanceExecution(address processAddress) public { + <%= nodeName(processId()) %>_Contract(processAddress).startExecution(); + } +} + +<% var contracts2Call = getContracts2Call(); + var subprocessToKill = getPossibleKillSubprocess(); + var eventsToCatch = getCatchingEvents(processId()); + var eventTypes = allEventTypes(); -%> + +contract <%= nodeName(processId()) %>_Contract is AbstractProcess { + + uint public marking = uint(<%= subprocessStartMarking(processId()) %>); + uint public startedActivities = 0; +<% if(contracts2Call.length > 0) { -%> + address[] private subInstanceAddresses; + mapping(uint => uint) private subInstanceStartedIndexes; +<% } -%> + +<% /* --------- Solidity events to throw when an end message event is thrown ---------------------------- */ -%> +<% var messages = getThrowingMessages(); + if(messages.length > 0) { + messages.forEach(nodeId => { -%> + // <%= nodeName(nodeId) %> + event <%= nodeId %>_Mesage(bytes32 messageText); +<% })} -%> + +<% /* --------- Fields for interacting with external resources by means of callbacks (Oracle) --------------- */ -%> +<% if(oracleInfo.size > 0) { -%> + mapping(uint => address) oracleAddresses; +<% } -%> + // Process Variables + <%= globalDeclarations() -%> + + function <%= nodeName(processId()) %>_Contract(address _parent, address _worklist, address _processRegistry) public AbstractProcess(_parent, _worklist, _processRegistry) { +<% oracleTaskMap.forEach((oracleKey, nodeId, map) => { -%> + oracleAddresses[<%= flowNodeIndex(nodeId) %>] = <%= oracleInfo.get(oracleKey).address %>; +<% }) -%> +<% if(getCountExternalTasks() > 0) { -%> + for (uint i = 0; i < <%= getCountExternalTasks() %>; i++) + requestedID.push(0); +<% } -%> + } + + function startExecution() public { + require(marking == uint(<%= subprocessStartMarking(processId()) %>) && startedActivities == 0); + step(uint(<%= subprocessStartMarking(processId()) %>), 0); + } + +<% /* --------------------------------- Event Handling ---------------------------------- */ -%> + function handleEvent(bytes32 code, bytes32 eventType, uint _instanceIndex, bool isInstanceCompleted) public { +<% if(contracts2Call.length > 0 ) { -%> + var (tmpMarking, tmpStartedActivities) = (marking, startedActivities); + uint maskIndex = uint(1) << _instanceIndex; + uint sourceChild = 0; +<% var first = true; + contracts2Call.forEach(nodeId => { + var inFirst = true; + var node = nodeMap.get(nodeId); -%> +<% if(first) { first = false; -%> if <% } else { -%> else if <% }-%>(subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] & uint(maskIndex) != 0) { + if(isInstanceCompleted) + subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] &= uint(~maskIndex); +<% if(is(node, 'bpmn:BoundaryEvent')) { -%> +<% var terminateCandidates = getTerminateCandidates(processId()); + if(terminateCandidates.length > 0 ) { + terminateCandidates.forEach(bEvtId => { + var evtParentId = nodeMap.get(bEvtId).$parent.id; + if (inFirst && evtParentId === node.$parent.id) { + inFirst = false; -%> + if(eventType == "Terminate") { + <% if(evtParentId === processId()) { -%> + killProcess(); + propagateEvent('Default', 'Default', 0, 0, <%= flowNodeIndex(nodeId) %>); + <% } else { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(evtParentId) %>), tmpMarking, tmpStartedActivities); + step(tmpMarking | uint(<%= postMarking(evtParentId) %>), tmpStartedActivities); + <% } -%> + } + <% } }) } -%> +<% } -%> +<% if(inFirst) { inFirst = false; -%> if <% } else { -%> else if <% }-%>(eventType == "Default") { +<% if (node.loopCharacteristics && node.loopCharacteristics.$type === 'bpmn:MultiInstanceLoopCharacteristics') { -%> +<% if (node.loopCharacteristics.isSequential) { -%> + if(++_instanceIndex < subInstanceAddresses.length && subInstanceAddresses[_instanceIndex] == 0) { + AbstractProcess child_<%= flowNodeIndex(nodeId) %> = AbstractProcess(AbstractRegistry(processRegistry).newInstanceFor(uint(<%= nodeRealIndex(nodeId) %>), this)); + subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] |= (uint(1) << _instanceIndex); + subInstanceAddresses[_instanceIndex] = child_<%= flowNodeIndex(nodeId) %>; + child_<%= flowNodeIndex(nodeId) %>.setInstanceIndex(_instanceIndex); + } + else { + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>)); + } +<% } else { -%> + if (subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] == 0) + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>)); +<% }} else if(!is(node, 'bpmn:BoundaryEvent') && !node.triggeredByEvent) { -%> + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>)); +<% } else { -%> + tmpStartedActivities &= uint(~<%= flowNodeIndex(nodeId) %>); + (marking, startedActivities) = (tmpMarking, tmpStartedActivities); + if(tmpMarking | tmpStartedActivities == 0) + propagateEvent("<%= nodeName(nodeId) %>", "Default", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } -%> + } +<% if(eventsToCatch.length > 0) { -%> + else + sourceChild |= uint(<%= flowNodeIndex(nodeId) %>); +<% } -%> + } +<% }) -%> +<% first = true; + eventsToCatch.forEach(evtId => { + var nodeEvt = nodeMap.get(evtId); + var evtType = eventType(evtId); -%> +<% if (evtType === 'Error' || evtType === 'Escalation') { + var candidateSubprocMask = getProcessCandidatesMaskFrom(evtId, evtType, nodeName(evtId), contracts2Call, eventsToCatch); -%> +<% if(first) { first = false; -%> if <% } else { -%> else if <% }-%>(eventType == "<%= evtType %>" && code == "<%= nodeName(evtId) %>" && sourceChild & uint(<%= candidateSubprocMask %>) != 0) { +<% if(isInterrupting(evtId)) { + if(nodeEvt.$parent.triggeredByEvent) { + var startingKill = nodeEvt.$parent.$parent.id === processId() ? 0 : flowNodeIndex(nodeEvt.$parent.$parent.id); -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= startingKill %>), tmpMarking, tmpStartedActivities); + step(tmpMarking | uint(<%= subprocessStartMarking(nodeEvt.$parent.id) %>), tmpStartedActivities | uint(<%= flowNodeIndex(nodeEvt.$parent.id) %>)); +<% } else { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(nodeEvt.attachedToRef.id) %>), tmpMarking, tmpStartedActivities); + step(tmpMarking | uint(<%= postMarking(evtId) %>), tmpStartedActivities); +<% } -%> +<% } else { -%> + createNewSubprocessInstance(<%= nodeRealIndex(evtId) %>); +<% if (nodeEvt.attachedToRef) { + let nodeId = nodeEvt.attachedToRef.id; + let node = nodeMap.get(nodeId); + if (node.loopCharacteristics && node.loopCharacteristics.$type === 'bpmn:MultiInstanceLoopCharacteristics') { + if (node.loopCharacteristics.isSequential) { -%> + if(++_instanceIndex < subInstanceAddresses.length && subInstanceAddresses[_instanceIndex] == 0) { + AbstractProcess child_<%= flowNodeIndex(nodeId) %> = AbstractProcess(AbstractRegistry(processRegistry).newInstanceFor(uint(<%= nodeRealIndex(nodeId) %>), this)); + subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] |= (uint(1) << _instanceIndex); + subInstanceAddresses[_instanceIndex] = child_<%= flowNodeIndex(nodeId) %>; + child_<%= flowNodeIndex(nodeId) %>.setInstanceIndex(_instanceIndex); + step(tmpMarking, tmpStartedActivities | uint(<%= flowNodeIndex(evtId) %>)); + } + else { + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>) | uint(<%= flowNodeIndex(evtId) %>)); + } +<% } else { -%> + if (subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] == 0) + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>) | uint(<%= flowNodeIndex(evtId) %>)); +<% }} else if(!is(node, 'bpmn:BoundaryEvent') && !node.triggeredByEvent) { -%> + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>) | uint(<%= flowNodeIndex(evtId) %>)); +<% } -%> +<% } else { -%> + step(tmpMarking, tmpStartedActivities | uint(<%= flowNodeIndex(evtId) %>)); +<% } } -%> + } +<% } -%> +<% }); -%> + else { + (tmpMarking, tmpStartedActivities) = propagateEvent(code, eventType, tmpMarking, tmpStartedActivities, sourceChild); + step(tmpMarking, tmpStartedActivities); + } +<% } else { -%> + // Process without calls to external contracts. + // No events to catch !!! +<% } -%> + } + +<% /* --------------------------------- Kill Childrens & Broadcast ------------------------------ */ -%> + function killProcess() public { + (marking, startedActivities) = killProcess(0, marking, startedActivities); + } + + function killProcess(uint processElementIndex, uint tmpMarking, uint tmpStartedActivities) internal returns(uint, uint) { +<% if(contracts2Call.length == 0) { /* Case No External contract call in the contract */ -%> + if(processElementIndex == 0) + tmpMarking = tmpStartedActivities = 0; +<% if(subprocessToKill.length > 0) { -%> +<% subprocessToKill.forEach(subprocId => { -%> + else if(processElementIndex & uint(<%= flowNodeIndex(subprocId) %>) != 0) { + tmpMarking &= uint(~<%=subprocessFullMarking(subprocId)%>); + tmpStartedActivities &= uint(~<%= subprocessNodeFullMarking(subprocId) %>); + } +<% }) } +} else { /* Case External Contract Calls in Main contract */ -%> + uint externalContracts2Stop = 0; + uint allAddresses2Stop = 0; + if(processElementIndex == 0) { + tmpMarking = tmpStartedActivities = 0; +<% var getContracts2CallMask = getContracts2CallMaskFrom(processId(), contracts2Call); + if(getContracts2CallMask > 0) { -%> + externalContracts2Stop = uint(<%= getContracts2CallMask %>); +<% } -%> + } +<% subprocessToKill.forEach(subprocId => { -%> + else if(processElementIndex & uint(<%= flowNodeIndex(subprocId) %>) != 0) { +<% if(callActivities.indexOf(subprocId) >= 0) { -%> + externalContracts2Stop = uint(<%= flowNodeIndex(subprocId) %>); +<% } else { -%> + tmpMarking &= uint(~<%= subprocessFullMarking(subprocId) %>); + tmpStartedActivities &= uint(~<%= subprocessNodeFullMarking(subprocId) %>); +<% var contracts2CallMask = getContracts2CallMaskFrom(subprocId, contracts2Call); + if (contracts2CallMask !== 0) { -%> + externalContracts2Stop = uint(<%= contracts2CallMask %>); +<% } -%> +<% } -%> + } +<% }) -%> +<% contracts2Call.forEach(subprocId => { -%> + if(externalContracts2Stop & uint(<%= flowNodeIndex(subprocId) %>) != 0) { + allAddresses2Stop |= subInstanceStartedIndexes[<%= nodeRealIndex(subprocId) %>]; + subInstanceStartedIndexes[<%= nodeRealIndex(subprocId) %>] = 0; + } +<% }) -%> + for(uint j = 0; j < subInstanceAddresses.length; j++) + if(allAddresses2Stop & (uint(1) << j) != 0) + AbstractProcess(subInstanceAddresses[j]).killProcess(); +<% } -%> + return (tmpMarking, tmpStartedActivities); + } + + function broadcastSignal() public { + var (tmpMarking, tmpStartedActivities) = broadcastSignal(marking, startedActivities, 0); + step(tmpMarking, tmpStartedActivities); + } + + function broadcastSignal(uint tmpMarking, uint tmpStartedActivities, uint sourceChild) internal returns(uint, uint) { +<% eventsToCatch.forEach(nodeId => { + if(eventType(nodeId) === 'Signal') { + var node = nodeMap.get(nodeId); + if(is(node, 'bpmn:IntermediateCatchEvent')) { + if (isPartOfDeferredChoice(nodeId)) { -%> + if(tmpMarking & uint(<%= deferredChoiceMarking(nodeId) %>) == uint(<%= deferredChoiceMarking(nodeId) %>)) + tmpMarking = tmpMarking & uint(~<%= deferredChoiceMarking(nodeId) %>) | uint(<%= postMarking(nodeId) %>); +<% var deferredNodeMarking = deferredChoiceNodeMarking(nodeId); + if(deferredNodeMarking != 0) { -%> + tmpStartedActivities &= uint(~<%= deferredNodeMarking %>); +<% } -%> +<% } else { -%> + if(tmpMarking & uint(<%= preMarking(nodeId) %>) == uint(<%= preMarking(nodeId) %>)) + tmpMarking = tmpMarking & uint(~<%= preMarking(nodeId) %>) | uint(<%= postMarking(nodeId) %>); +<% }} else if(is(node, 'bpmn:StartEvent')) { + var parent = nodeMap.get(node.$parent.id); + if (parent.triggeredByEvent) { + if (isInterrupting(nodeId)) { -%> + if(tmpMarking & uint(~<%= subprocessFullMarking(parent.id) %>) != 0 || tmpStartedActivities & uint(~<%= subprocessNodeFullMarking(parent.id) %>) != 0) { + (tmpMarking, tmpStartedActivities) = killProcess(0, tmpMarking, tmpStartedActivities); + tmpStartedActivities |= uint(<%= flowNodeIndex(parent.id) %>); + tmpMarking |= uint(<%= subprocessStartMarking(parent.id) %>); + } +<% } else { -%> + if(tmpMarking & uint(<%= subprocessFullMarking(processId()) %>) != 0 || tmpStartedActivities & uint(~<%= flowNodeIndex(parent.id) %>) != 0) { + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); + tmpStartedActivities |= <%= flowNodeIndex(nodeId) %>; + } +<% } -%> +<% } else { -%> + /* Starting subprocess (not event subprocess) */ +<% } -%> +<% } else if (is(node, 'bpmn:BoundaryEvent')) { -%> + if(tmpStartedActivities & uint(<%= flowNodeIndex(node.attachedToRef.id) %>) != 0) { +<% if(isInterrupting(nodeId)) { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(node.attachedToRef.id) %>), tmpMarking, tmpStartedActivities); + tmpMarking |= uint(<%= postMarking(nodeId) %>); +<% } else { -%> + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeId) %>); +<% } -%> + } +<% } -%> +<% }}) -%> +<% if (contracts2Call.length > 0) { -%> + uint contracts2Broadcast = 0; +<% contracts2Call.forEach(nodeId => { -%> + if (tmpStartedActivities & uint(<%= flowNodeIndex(nodeId) %>) != 0) + contracts2Broadcast |= subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>]; +<% }) -%> + contracts2Broadcast &= uint(~sourceChild); + if(contracts2Broadcast != 0) + for(uint j = 0; j < subInstanceAddresses.length; j++) + if(contracts2Broadcast & (uint(1) << j) != 0) + AbstractProcess(subInstanceAddresses[j]).broadcastSignal(); +<% } -%> + return (tmpMarking, tmpStartedActivities); + } + +<% /* --------------------------------- Functions from BPMN elements -------------------------- */ -%> +<% nodeList.forEach( nodeId => { + let node = nodeMap.get(nodeId), + nodePreMarking = preMarking(nodeId), + nodePostMarking = postMarking(nodeId); + if (is(node, 'bpmn:ServiceTask')) { -%> + // <%= nodeName(node.id) %> + function <%= nodeName(node.id) %>_callbak (uint reqId<%= concatParameters(name2Ids.get(nodeName[0]), false, true, true) %>) external returns(bool) { + require(msg.sender == oracleAddresses[<%= flowNodeIndex(node.id) %>]); + uint index = uint(1) << reqId; + if(requestedID[<%= nodeRealIndex(node.id) %>] & index == index) { + <%= nodeFunctionBody(nodeId) %> + requestedID[<%= nodeRealIndex(node.id) %>] &= ~uint(index); + step(marking | uint(<%= nodePostMarking %>), startedActivities); + return true; + } + return false ; + } +<% }}); -%> + +<% boundaryMessages = getMessages(); + groupedIds = getWorkItemsGroupByParameters(false); + groupedIds.forEach(idGroup => { -%> + function <%= nodeName(idGroup[0]) %>_complete(uint elementIndex<%= concatParameters(idGroup[0], false, true, true) %>) external { + var (tmpMarking, tmpStartedActivities) = (marking, startedActivities); +<% idGroup.forEach(nodeId => { + var node = nodeMap.get(nodeId); -%> + if(elementIndex == uint(<%= nodeRealIndex(nodeId) %>)) { + require(msg.sender == worklist && tmpStartedActivities & uint(<%= flowNodeIndex(nodeId) %>) != 0); +<% if(boundaryMessages.indexOf(nodeId) < 0) { + var functionBody = nodeFunctionBody(nodeId); + if(functionBody.length > 0) { -%> + <%- functionBody %> +<% } -%> +<% if (isPartOfDeferredChoice(nodeId)) { + var deferredNodeMarking = deferredChoiceNodeMarking(nodeId); + if(deferredNodeMarking != 0) { -%> + step(tmpMarking & uint(~<%= deferredChoiceMarking(nodeId) %>) | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= deferredNodeMarking %>)); +<% } else { -%> + step(tmpMarking & uint(~<%= deferredChoiceMarking(nodeId) %>) | uint(<%= postMarking(nodeId) %>), tmpStartedActivities); +<% } -%> +<% } else { -%> + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities & uint(~<%= flowNodeIndex(nodeId) %>)); +<% }} else { + var parent = nodeMap.get(node.$parent.id); + if(parent.triggeredByEvent) { -%> +<% if (isInterrupting(nodeId)) { -%> + killProcess(); + step(uint(<%= subprocessStartMarking(parent.id) %>), uint(<%= flowNodeIndex(parent.id) %>)); +<% } else { -%> + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); + step(tmpMarking, tmpStartedActivities | <%= flowNodeIndex(parent.id) %>); +<% } -%> +<% } else { + var attachedTo = node.attachedToRef.id; + if (isInterrupting(nodeId)) { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(attachedTo) %>), tmpMarking, tmpStartedActivities); + step(tmpMarking | uint(<%= postMarking(nodeId) %>), tmpStartedActivities); +<% } else { -%> + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); + step(tmpMarking, tmpStartedActivities | <%= flowNodeIndex(nodeId) %>); +<% }}} -%> + return; + } +<% }) -%> + } +<% }) -%> + +<% if(contracts2Call.length > 0) { -%> + function createNewSubprocessInstance(uint nodeIndex) private { + AbstractProcess child = AbstractProcess(AbstractRegistry(processRegistry).newInstanceFor(nodeIndex, this)); + uint index = subInstanceAddresses.length; + subInstanceAddresses.push(child); + subInstanceStartedIndexes[nodeIndex] |= (uint(1) << index); + child.setInstanceIndex(index); + } +<% } -%> + +<% /* ----------------- Step: Method to execute internal activities automatically ------------------ */ -%> + function step(uint tmpMarking, uint tmpStartedActivities) internal { + while (true) { +<% nodeList.forEach(nodeId => { + var node = nodeMap.get(nodeId); + var nodePostMarking = postMarking(nodeId); + if(catchingMessages.indexOf(nodeId) < 0) { + var nodePreMarking = preMarking(nodeId); + if (node.loopCharacteristics && node.loopCharacteristics.$type === 'bpmn:MultiInstanceLoopCharacteristics') { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { +<% if(!is (node, "bpmn:Task")) { -%> + for(uint i = 0; i < <%= node.loopCharacteristics.loopCardinality ? node.loopCharacteristics.loopCardinality.body : 1 %>; i++) { +<% if (node.loopCharacteristics.isSequential) { -%> + subInstanceAddresses.push(0); +<% } else { -%> + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); +<% } -%> + } +<% if (node.loopCharacteristics.isSequential) { -%> + AbstractProcess child_<%= flowNodeIndex(nodeId) %> = AbstractProcess(AbstractRegistry(processRegistry).newInstanceFor(uint(<%= nodeRealIndex(nodeId) %>), this)); + uint index = subInstanceAddresses.length - <%= node.loopCharacteristics.loopCardinality ? node.loopCharacteristics.loopCardinality.body : 1 %>; + subInstanceStartedIndexes[<%= nodeRealIndex(nodeId) %>] |= (uint(1) << index); + subInstanceAddresses[index] = child_<%= flowNodeIndex(nodeId) %>; + child_<%= flowNodeIndex(nodeId) %>.setInstanceIndex(index); +<% } -%> + tmpMarking &= uint(~<%= nodePreMarking %>); + tmpStartedActivities |= <%= flowNodeIndex(nodeId) %>; +<% } -%> + continue; + } +<% } else if (is(node, 'bpmn:ExclusiveGateway')) { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { + tmpMarking &= uint(~<%= nodePreMarking %>); +<% if (node.outgoing && node.outgoing.length > 1) { + var i = 0; node.outgoing.forEach( outgoing => { -%> +<%= i > 0 ? 'else ' : '' %><%if (i < node.outgoing.length - 1){ %>if (<%- getCondition(outgoing) %>)<% } -%> + tmpMarking |= uint(<%= flowEdgeIndex(outgoing.id) %>); +<% i++;}) -%> +<% } else { -%> + tmpMarking |= uint(<%= postMarking(nodeId) %>); +<% } -%> + continue; + } +<% } else if(is(node, 'bpmn:ParallelGateway') || is(node,'bpmn:EventBasedGateway')) { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) == uint(<%= nodePreMarking %>)) { + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= nodePostMarking %>); + continue; + } +<% } else if(is(node, 'bpmn:SubProcess') && !node.triggeredByEvent && multiinstanceActivities.indexOf(nodeId) < 0) { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeId) %>); + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= subprocessStartMarking(nodeId) %>); + continue; + } +<% } else if(is(node, 'bpmn:ReceiveTask')) { + if (isPartOfDeferredChoice(nodeId)) { -%> + if (tmpStartedActivities & uint(<%= flowNodeIndex(nodeId) %>) == 0 && tmpMarking & uint(<%= deferredChoiceMarking(nodeId) %>) == uint(<%= deferredChoiceMarking(nodeId) %>)) { + <%= nodeName(processId()) %>_AbstractWorlist(worklist).<%= nodeName(nodeId) %>_start(<%= nodeRealIndex(nodeId) %><%= concatParameters(nodeId, true, false, true) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeId) %>); + continue; + } +<% } else { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { + <%= nodeName(processId()) %>_AbstractWorlist(worklist).<%= nodeName(nodeId) %>_start(<%= nodeRealIndex(nodeId) %><%= concatParameters(nodeId, true, false, true) %>); + tmpMarking &= uint(~<%= nodePreMarking %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(node.id) %>); + continue; + } +<% }} else if((is(node, 'bpmn:Task'))) { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { +<% if (is(node, 'bpmn:UserTask')) { -%> + <%= nodeName(processId()) %>_AbstractWorlist(worklist).<%= nodeName(nodeId) %>_start(<%= nodeRealIndex(nodeId) %><%= concatParameters(nodeId, true, false, true) %>); + tmpMarking &= uint(~<%= nodePreMarking %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(node.id) %>); +<% } else if(is(node, 'bpmn:ServiceTask')) { + var functName = oracleTaskMap.get(nodeId); + var localInfo = oracleInfo.get(functName); -%> + uint reqId = Oracle_Wrapper(oracleAddresses[<%= flowNodeIndex(nodeId) %>]).<%= localInfo.functionName %>(<%= concatParameters(node.id, true, false, false) %>, this.<%= nodeName(nodeId) %>_callbak); + requestedID[<%= nodeRealIndex(node.id) %>] |= uint(1) << reqId; + tmpMarking &= uint(~<%= nodePreMarking %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(node.id) %>); +<% } else if (is(node, 'bpmn:ScriptTask')) { + if (node.script) { -%> + <%- nodeFunctionBody(nodeId) -%> +<% } -%> + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= nodePostMarking %>); +<% } else { -%> + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= nodePostMarking %>); +<% } -%> + continue; + } +<% } else if(is(node, 'bpmn:EndEvent')) { + var evtType = eventType(nodeId); -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { +<% if(evtType === 'Default' || evtType === 'Message') { -%> +<% if(evtType === 'Message') { -%> + <%= nodeId %>_Mesage('<%= nodeName(nodeId) %>'); +<% } -%> + tmpMarking &= uint(~<%= nodePreMarking %>); + if (tmpMarking & uint(<%= subprocessFullMarking(node.$parent.id) %>) == 0 && tmpStartedActivities & uint(<%= subprocessNodeMarking(node.$parent.id) %>) == 0) { +<% if(node.$parent.id === processId() || nodeMap.get(node.$parent.id).triggeredByEvent || (is(nodeMap.get(processId()), 'bpmn:BoundaryEvent') && node.$parent.id === nodeMap.get(processId()).$parent.id)) { -%> +<% if(nodeMap.get(node.$parent.id).triggeredByEvent) { -%> + tmpStartedActivities &= uint(~<%= flowNodeIndex(node.$parent.id) %>); +<% } -%> + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "Default", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else { -%> + tmpStartedActivities &= uint(~<%= flowNodeIndex(node.$parent.id) %>); + tmpMarking |= uint(<%= postMarking(node.$parent.id) %>); +<% } -%> + } +<% } else if (evtType === 'Terminate') { -%> +<% if(is(nodeMap.get(processId()), 'bpmn:BoundaryEvent') && node.$parent.id === nodeMap.get(processId()).$parent.id) { -%> + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "Terminate", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else if(node.$parent.id === processId()) { -%> + (tmpMarking, tmpStartedActivities) = killProcess(0, tmpMarking, tmpStartedActivities); + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "Default", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(node.$parent.id) %>), tmpMarking, tmpStartedActivities); + tmpMarking |= uint(<%= postMarking(node.$parent.id) %>); + tmpStartedActivities &= uint(~<%= flowNodeIndex(node.$parent.id) %>); +<% } -%> +<% } else if (evtType === 'Signal') { -%> + tmpMarking &= uint(~<%= nodePreMarking %>); + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "Signal", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else if (evtType === 'Error' || evtType === 'Escalation') { + var localEvents2Catch = getCatchingEventsFrom(node.$parent.id, evtType, nodeName(nodeId)); + if(localEvents2Catch.length == 0) { -%> + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "<%= evtType %>", tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else { + localEvents2Catch.forEach(evtId => { + var nodeEvt = nodeMap.get(evtId); + if(isInterrupting(evtId)) { + if(nodeEvt.$parent.triggeredByEvent) { + var startingKill = nodeEvt.$parent.$parent.id === processId() ? 0 : flowNodeIndex(nodeEvt.$parent.$parent.id); -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= startingKill %>), tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities); + tmpMarking |= uint(<%= subprocessStartMarking(nodeEvt.$parent.id) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeEvt.$parent.id) %>); +<% } else { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(nodeEvt.attachedToRef.id) %>), tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities); + tmpMarking |= uint(<%= postMarking(evtId) %>); +<% } -%> +<% } else { -%> + tmpMarking &= uint(~<%= nodePreMarking %>); + createNewSubprocessInstance(<%= nodeRealIndex(evtId) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(evtId) %>); +<% } }) -%> +<% } } -%> + continue; + } +<% } else if(is(node, 'bpmn:IntermediateThrowEvent')) { + var evtType = eventType(nodeId); -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { +<% if(evtType === 'Default') { -%> + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= nodePostMarking %>); +<% } else { + if (isPartOfDeferredChoice(nodeId)) { -%> + tmpMarking = tmpMarking & uint(~<%= deferredChoiceMarking(nodeId) %>) | uint(<%= nodePostMarking %>); +<% var deferredNodeMarking = deferredChoiceNodeMarking(nodeId); + if(deferredNodeMarking != 0) { -%> + tmpStartedActivities &= uint(~<%= deferredNodeMarking %>); +<% } -%> +<% } else { -%> + tmpMarking = tmpMarking & uint(~<%= nodePreMarking %>) | uint(<%= nodePostMarking %>); +<% } if (evtType === 'Signal') { -%> + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "Signal", tmpMarking, tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); +<% } else if(evtType === 'Message') { -%> + <%= nodeId %>_Mesage('<%= nodeName(nodeId) %>'); +<% } else if(evtType === 'Escalation') { + var localEvents2Catch = getCatchingEventsFrom(node.$parent.id, evtType, nodeName(nodeId)); + if(localEvents2Catch.length == 0) { -%> + (tmpMarking, tmpStartedActivities) = propagateEvent("<%= nodeName(nodeId) %>", "<%= evtType %>", tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities, uint(<%= flowNodeIndex(nodeId) %>)); + <% } else { + localEvents2Catch.forEach(evtId => { + var nodeEvt = nodeMap.get(evtId); + if(isInterrupting(evtId)) { + if(nodeEvt.$parent.triggeredByEvent) { + var startingKill = nodeEvt.$parent.$parent.id === processId() ? 0 : flowNodeIndex(nodeEvt.$parent.$parent.id); -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= startingKill %>), tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities); + tmpMarking |= uint(<%= subprocessStartMarking(nodeEvt.$parent.id) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeEvt.$parent.id) %>); + <% } else { -%> + (tmpMarking, tmpStartedActivities) = killProcess(uint(<%= flowNodeIndex(nodeEvt.attachedToRef.id) %>), tmpMarking & uint(~<%= nodePreMarking %>), tmpStartedActivities); + tmpMarking |= uint(<%= postMarking(evtId) %>); + <% } -%> + <% } else { -%> + tmpMarking &= uint(~<%= nodePreMarking %>); + createNewSubprocessInstance(<%= nodeRealIndex(evtId) %>); + <% if(nodeEvt.$parent.triggeredByEvent) { -%> + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeEvt.$parent.id) %>); + <% } else { -%> + tmpStartedActivities |= uint(<%= flowNodeIndex(evtId) %>); + <% } } }) -%> + <% } -%> +<% }} -%> + continue; + } +<% } else if (callActivities.indexOf(nodeId) >= 0) { -%> + if (tmpMarking & uint(<%= nodePreMarking %>) != 0) { + createNewSubprocessInstance(<%= nodeRealIndex(nodeId) %>); + tmpMarking &= uint(~<%= nodePreMarking %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeId) %>); + continue; + } +<% }} else { + if(isBoundaryEvent(nodeId)) { + var attachedTo = node.attachedToRef.id; -%> + if (tmpStartedActivities & uint(<%= flowNodeIndex(attachedTo) %>) != 0 && tmpStartedActivities & uint(<%= flowNodeIndex(nodeId) %>) == 0) { + <%= nodeName(processId()) %>_AbstractWorlist(worklist).<%= nodeName(nodeId) %>_start(<%= nodeRealIndex(nodeId) %><%= concatParameters(nodeId, true, false, true) %>); + tmpStartedActivities |= uint(<%= flowNodeIndex(nodeId) %>); + continue; + } + if (tmpStartedActivities & uint(<%= flowNodeIndex(attachedTo) %>) == 0 && tmpStartedActivities & uint(<%= flowNodeIndex(nodeId) %>) != 0) { + tmpStartedActivities &= uint(~<%= flowNodeIndex(nodeId) %>); + continue; + } + <% } -%> +<% }}); -%> + break; + } + if(marking != 0 || startedActivities != 0) { + marking = tmpMarking; + startedActivities = tmpStartedActivities; + } + } + + function getWorklistAddress() external view returns(address) { + return worklist; + } + + function getInstanceIndex() external view returns(uint) { + return instanceIndex; + } + +<% /* ------------- Methods to obtain information about multiple contract instances running ------------------ */ -%> +<% if (callActivities.length > 0 || multiinstanceActivities.length > 0 || nonInterruptingEvents.length > 0) { -%> + function allInstanceAddresses() external view returns(address []) { + return subInstanceAddresses; + } + + function startedInstanceIndexFor(uint instanceNode) external view returns(uint) { + return subInstanceStartedIndexes[instanceNode]; + } + +<% } -%> +} diff --git a/v3.0/caterpillar-core/templates/bpmn2solIData.ejs b/v3.0/caterpillar-core/templates/bpmn2solIData.ejs new file mode 100644 index 0000000..2db1d34 --- /dev/null +++ b/v3.0/caterpillar-core/templates/bpmn2solIData.ejs @@ -0,0 +1,81 @@ +pragma solidity ^0.4.25; + +import 'IData'; + +contract <%= contractName %>Data is IDataImp { +<% globalFields.forEach(param => { -%> + <%= param.type %> <%= param.name %>; +<% }) -%> + + function executeScript(uint eInd) public returns(uint) { +<% taskScripts.forEach((script, cInd) => { -%> + if (eInd == <%= cInd%>) { + <%= script %> + return <%= postC(cInd) %>; + } +<% }) -%> +<% gatewayScript.forEach((scripts, cInd) => { -%> + if (eInd == <%= cInd%>) { +<% scripts.forEach(arcInf => { -%> + if (<%= arcInf.script %>) + return <%= elementBitMask(arcInf.edgeInd) %>; +<% }); -%> + } +<% }); -%> + } + +<% let checkIn = checkInList; + checkIn.forEach(funcInfo => { -%> + function checkIn(uint eInd<%= checkInParams(funcInfo.params, funcInfo.renamedParams) %>) public { + require (<%= indexArrayToBitSet(funcInfo.eIndexes) %> & (1 << eInd) != 0); +<% if(funcInfo.scripts.length > 0 && funcInfo.scripts[0] === undefined) { -%> +<% } else if (funcInfo.eIndexes.length == 1) { + if(funcInfo.scripts.length > 0) { -%> + <%= funcInfo.scripts[0] %> +<% }} else { %> +<% let first = true; + for(let i = 0; i < funcInfo.eIndexes.length; i++) { -%> +<% if(first) { first = false; -%> + if (eInd == <%= funcInfo.eIndexes[i] %>) { + <%= funcInfo.scripts[i] %> + } +<% } else { -%> + else if (eInd == <%= funcInfo.eIndexes[i] %>) { + <%= funcInfo.scripts[i] %> + } +<% } -%> +<% }} -%> + continueExecution(eInd); + } + +<% }) -%> +<% let checkOut = checkOutList; + checkOut.forEach(funcInfo => { -%> + function <%= funcInfo.funcName %>(uint eInd) public view <%= checkOutReturn(funcInfo.types, 'returns') %> { + require (<%= indexArrayToBitSet(funcInfo.eIndexes) %> & (1 << eInd) == 0); +<% if (funcInfo.eIndexes.length == 1) { + if(funcInfo.types.length > 0) { -%> + <%= checkOutReturn(funcInfo.paramNames[0], 'return') %>; +<% }} else { -%> +<% let first = true; + for(let i = 0; i < funcInfo.eIndexes.length; i++) { + if(funcInfo.types.length > 0) { + if (i === funcInfo.eIndexes.length - 1) { -%> + else + <%= checkOutReturn(funcInfo.paramNames[i], 'return') %>; +<% } else { -%> +<% if (first) { + first = false; -%> + if (eInd == <%= funcInfo.eIndexes[i] %>) +<% } else { -%> + else if (eInd == <%= funcInfo.eIndexes[i] %>) +<% } -%> + <%= checkOutReturn(funcInfo.paramNames[i], 'return') %>; +<% } -%> +<% }} -%> +<% } -%> + } + +<% }); -%> +} + diff --git a/v3.0/caterpillar-core/templates/factory2Solidity.ejs b/v3.0/caterpillar-core/templates/factory2Solidity.ejs new file mode 100644 index 0000000..9c3e2a9 --- /dev/null +++ b/v3.0/caterpillar-core/templates/factory2Solidity.ejs @@ -0,0 +1,11 @@ +pragma solidity ^0.4.25; + +import '<%= contractName %>Data'; + +contract <%= contractName %>Factory { + + function newInstance() public returns(address) { + return new <%= contractName %>Data(); + } + +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/templates/policy2sol.ejs b/v3.0/caterpillar-core/templates/policy2sol.ejs new file mode 100644 index 0000000..217963c --- /dev/null +++ b/v3.0/caterpillar-core/templates/policy2sol.ejs @@ -0,0 +1,127 @@ +pragma solidity ^0.4.25; + +contract <%= policyName %>_Contract { + + function isCaseCreator(uint roleIndex) public pure returns(bool) { + return <%= creatorMask %> & (uint(1) << roleIndex) == <%= creatorMask %>; + } + + function canNominate (uint rNominator, uint rNominee) public pure returns(bool) { + uint nomineeMask = uint(1) << rNominee; +<% taken = []; + nominationStatements.forEach(statement => { + if(taken[roleIndex(statement.nominator)] != 1) { + taken[roleIndex(statement.nominator)] = 1; -%> + if (rNominator == <%= roleIndex(statement.nominator) %>) + return nomineeMask & <%= nominationMask(statement.nominator, nominationStatements) %> != 0; +<% } -%> +<% }) -%> + return false; + } + + function assertNConstraint (uint rNominator, uint rNominee, uint nomineeRoles) public pure returns(bool) { + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); +<% nominationStatements.forEach(statement => { + if(statement.bindingConstraint !== undefined) { -%> + if (nominatorNomineeMask == <%= statementMask(statement) %>) +<% setMask = disjunctionSetMask(statement.bindingConstraint); + first = true; -%> + return<% if(statement.bindingConstraint.isNegative) { %>!(<% } setMask.forEach(andS => { -%> <%= !first ? '||' : '' -%> nomineeRoles & <%= andS %> == <%= andS %><% first = false;}) -%><% if(statement.bindingConstraint.isNegative) { %>)<% } -%>; +<% }}) -%> + return true; + } + + function requireNEndorsement (uint rNominator, uint rNominee) public pure returns(bool) { + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); +<% if(nominationStatements != undefined) { + candidates = endorsementRequiredMask(nominationStatements); + first = true; + if(candidates.length > 0) { -%> + return<% candidates.forEach(andS => { -%> <%= !first ? '||' : '' -%> nominatorNomineeMask == <%= andS %><% first = false;}) -%>; +<% } else { -%> + return false; +<% }}-%> + } + + function assertNVote (uint rNominator, uint rNominee, uint rEndorser, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(uint) { + uint endorserMask = uint(1) << rEndorser; + require((endorsedBy | rejectedBy) & endorserMask == 0); + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); + endorsedBy |= endorserMask; + rejectedBy |= endorserMask; +<% elseCond = false; + nominationStatements.forEach(statement => { + if(statement.endorsementConstraint !== undefined) { -%> + <%= elseCond ? 'else ' : '' %>if (nominatorNomineeMask == <%= statementMask(statement) %>) { + require(endorserMask & <%= disjunctionSetJoinMask(statement.endorsementConstraint) %> != 0); +<% setMask = disjunctionSetMask(statement.endorsementConstraint); + fOr = true; fAnd = true -%> + if (isAccepted && (<% setMask.forEach(andS => { -%><%= !fOr ? '|| ' : '' -%>endorsedBy & <%= andS %> == <%= andS %><% fOr = false;}) -%>)) + return 3; + else if (!isAccepted && (<% setMask.forEach(andS => { -%><% if(!fAnd) { -%> && <% } -%>rejectedBy & <%= andS %> != 0<% fAnd = false;}) -%>)) + return 0; + } +<% elseCond = true; }}) -%> + return 2; + } + + function canRelease (uint rNominator, uint rNominee) public pure returns(bool) { + uint nomineeMask = uint(1) << rNominee; +<% taken = []; + releaseStatements.forEach(statement => { + if(taken[roleIndex(statement.nominator)] != 1) { + taken[roleIndex(statement.nominator)] = 1; -%> + if (rNominator == <%= roleIndex(statement.nominator) %>) + return nomineeMask & <%= nominationMask(statement.nominator, releaseStatements) %> != 0; +<% } -%> +<% }) -%> + return false; + } + + function assertRConstraint (uint rNominator, uint rNominee, uint nomineeRoles) public pure returns(bool) { + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); +<% releaseStatements.forEach(statement => { + if(statement.bindingConstraint !== undefined) { -%> + if (nominatorNomineeMask == <%= statementMask(statement) %>) +<% setMask = disjunctionSetMask(statement.bindingConstraint); + first = true; -%> + return<% if(statement.bindingConstraint.isNegative) { %>!(<% } setMask.forEach(andS => { -%> <%= !first ? '||' : '' -%> nomineeRoles & <%= andS %> == <%= andS %><% first = false;}) -%><% if(statement.bindingConstraint.isNegative) { %>)<% } -%>; +<% }}) -%> + return true; + } + + function requireREndorsement (uint rNominator, uint rNominee) public pure returns(bool) { + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); +<% if(releaseStatements != undefined) { + candidates = endorsementRequiredMask(releaseStatements); + first = true; + if(candidates.length > 0) { -%> + return<% candidates.forEach(andS => { -%> <%= !first ? '||' : '' -%> nominatorNomineeMask == <%= andS %><% first = false;}) -%>; +<% } else { -%> + return false; +<% }}-%> + } + + function assertRVote (uint rNominator, uint rNominee, uint rEndorser, uint endorsedBy, uint rejectedBy, bool isAccepted) public pure returns(uint) { + uint endorserMask = uint(1) << rEndorser; + require((endorsedBy | rejectedBy) & endorserMask == 0); + uint nominatorNomineeMask = (uint(1) << rNominator) | (uint(1) << rNominee); + endorsedBy |= endorserMask; + rejectedBy |= endorserMask; +<% elseCond = false; + releaseStatements.forEach(statement => { + if(statement.endorsementConstraint !== undefined) { -%> + <%= elseCond ? 'else ' : '' %>if (nominatorNomineeMask == <%= statementMask(statement) %>) { + require(endorserMask & <%= disjunctionSetJoinMask(statement.endorsementConstraint) %> != 0); +<% setMask = disjunctionSetMask(statement.endorsementConstraint); + fOr = true; fAnd = true -%> + if (isAccepted && (<% setMask.forEach(andS => { -%><%= !fOr ? '|| ' : '' -%>endorsedBy & <%= andS %> == <%= andS %><% fOr = false;}) -%>)) + return 0; + else if (!isAccepted && (<% setMask.forEach(andS => { -%><% if(!fAnd) { -%> && <% } -%>rejectedBy & <%= andS %> != 0<% fAnd = false;}) -%>)) + return 3; + } +<% elseCond = true; }}) -%> + return 2; + } + +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/templates/procesRole2sol.ejs b/v3.0/caterpillar-core/templates/procesRole2sol.ejs new file mode 100644 index 0000000..321bd1f --- /dev/null +++ b/v3.0/caterpillar-core/templates/procesRole2sol.ejs @@ -0,0 +1,16 @@ +pragma solidity ^0.4.25; + +contract <%= contractName %>_Contract { + + function getRoleFromTask(uint taskIndex, bytes32 processId) public pure returns(uint) { +<% for (let [key, indexes] of processData) { -%> + if (processId == '<%= key %>') { +<% indexes.forEach(index => { -%> + if (taskIndex == <%= index.taskIndex %>) + return <%= index.roleIndex %>; +<% }) -%> + } +<% } -%> + return 0; + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/templates/processRole2solArray.ejs b/v3.0/caterpillar-core/templates/processRole2solArray.ejs new file mode 100644 index 0000000..b3feda7 --- /dev/null +++ b/v3.0/caterpillar-core/templates/processRole2solArray.ejs @@ -0,0 +1,16 @@ +pragma solidity ^0.4.25; + +contract <%= contractName %>_Contract { + + function getRoleFromTask(uint taskIndex, bytes32 processId) public pure returns(uint) { +<% for (let [key, indexes] of processData) { + let first = true; -%> + if (processId == '<%= key %>') { + uint[<%= indexes.length %>] memory I<%= key %> = [<% indexes.forEach(ind => { if (!first) { %>, <%= ind %><% } else { first = false; %>uint(<%= ind %>)<% } }) %>]; + if(taskIndex < <%= indexes.length %>) + return I<%= key %>[taskIndex]; + } +<% } -%> + return 0; + } +} \ No newline at end of file diff --git a/v3.0/caterpillar-core/templates/workList2sol.ejs b/v3.0/caterpillar-core/templates/workList2sol.ejs new file mode 100644 index 0000000..0c8bc16 --- /dev/null +++ b/v3.0/caterpillar-core/templates/workList2sol.ejs @@ -0,0 +1,49 @@ +pragma solidity ^0.4.25; + +import "AbstractWorklist"; + +contract <%= nodeName(processId()) %>_AbstractWorlist { + + <% groupedIds = getWorkItemsGroupByParameters(true); + groupedIds.forEach(idGroup => { + var nodeId = idGroup[0]; -%> + function <%= nodeName(nodeId) %>_start(uint<%= getParameterType(nodeId, true, true, true) %>) external; + <% }) -%> + + <% groupedIds = getWorkItemsGroupByParameters(false); + groupedIds.forEach(idGroup => { + var nodeId = idGroup[0]; -%> + function <%= nodeName(nodeId) %>_complete(uint<%= getParameterType(nodeId, false, true, true) %>) external; + <% }) -%> + +} + +contract <%= nodeName(processId()) %>_Worklist is AbstractWorklist { + + // Events with the information to include in the Log when a workitem is registered +<% groupedIds = getWorkItemsGroupByParameters(true); + groupedIds.forEach(idGroup => { -%> + event <%= nodeName(idGroup[0]) %>_Requested(uint<%= getParameterType(idGroup[0], true, true, true) %>); +<% }) -%> + +<% groupedIds = getWorkItemsGroupByParameters(true); + groupedIds.forEach(idGroup => { + var nodeId = idGroup[0]; -%> + function <%= nodeName(nodeId) %>_start(uint elementIndex<%= getParameters(nodeId, true, true, true) %>) external { + workitems.push(Workitem(elementIndex, msg.sender)); + <%= nodeName(nodeId) %>_Requested(workitems.length - 1<%= getParameters(nodeId, true, false, true) %>); + } +<% }) -%> + +<% groupedIds = getWorkItemsGroupByParameters(false); + groupedIds.forEach(idGroup => { + var nodeId = idGroup[0]; -%> + function <%= nodeName(nodeId) %>(uint workitemId<%= getParameters(nodeId, false, true, true) %>) external { + require(workitemId < workitems.length && workitems[workitemId].processInstanceAddr != address(0) && + canPerform(msg.sender, workitems[workitemId].processInstanceAddr, workitems[workitemId].elementIndex)); + <%= nodeName(processId()) %>_AbstractWorlist(workitems[workitemId].processInstanceAddr).<%= nodeName(nodeId) %>_complete(workitems[workitemId].elementIndex<%= getParameters(nodeId, false, false, true) %>); + workitems[workitemId].processInstanceAddr = address(0); + } +<% }) -%> + +} diff --git a/v3.0/caterpillar-core/tests/test.ts b/v3.0/caterpillar-core/tests/test.ts new file mode 100644 index 0000000..1c27008 --- /dev/null +++ b/v3.0/caterpillar-core/tests/test.ts @@ -0,0 +1,16 @@ + +import * as assert from 'assert'; +import * as mocha from 'mocha'; + +import { parseBpmn } from '../src/models/models.parsers'; +import * as fs from 'fs'; + +const example = fs.readFileSync('example.bpmn', 'utf-8'); + +describe('bpmn compilers', () => { + it('', () => { + let definitions = parseBpmn(example); + + assert.notEqual(definitions, null); + }); +}); \ No newline at end of file diff --git a/v3.0/caterpillar-core/tests/tsconfig.json b/v3.0/caterpillar-core/tests/tsconfig.json new file mode 100644 index 0000000..ba9bad5 --- /dev/null +++ b/v3.0/caterpillar-core/tests/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "outDir": "./out", + "lib": [ + "es6" + ], + "sourceMap": true, + "rootDir": "." + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/v3.0/execution-panel/.angular-cli.json b/v3.0/execution-panel/.angular-cli.json new file mode 100644 index 0000000..f0839b8 --- /dev/null +++ b/v3.0/execution-panel/.angular-cli.json @@ -0,0 +1,65 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "project": { + "name": "bpsol-client" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "assets": [ + "assets", + "favicon.ico" + ], + "index": "index.html", + "main": "main.ts", + "polyfills": "polyfills.ts", + "test": "test.ts", + "tsconfig": "tsconfig.app.json", + "testTsconfig": "tsconfig.spec.json", + "prefix": "app", + "styles": [ + "styles.css", + "../node_modules/bootstrap/dist/css/bootstrap.min.css", + "../node_modules/bpmn-js/assets/bpmn-font/css/bpmn.css", + "../node_modules/bpmn-js/assets/bpmn-font/css/bpmn-embedded.css", + "../node_modules/bpmn-js-properties-panel/styles/properties.less", + "../node_modules/diagram-js/assets/diagram-js.css" + ], + "scripts": [ + "../node_modules/jquery/dist/jquery.min.js", + "../node_modules/bootstrap/dist/js/bootstrap.min.js" + ], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "e2e": { + "protractor": { + "config": "./protractor.conf.js" + } + }, + "lint": [ + { + "project": "src/tsconfig.app.json" + }, + { + "project": "src/tsconfig.spec.json" + }, + { + "project": "e2e/tsconfig.e2e.json" + } + ], + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "css", + "component": {} + } +} diff --git a/v3.0/execution-panel/.editorconfig b/v3.0/execution-panel/.editorconfig new file mode 100644 index 0000000..6e87a00 --- /dev/null +++ b/v3.0/execution-panel/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/v3.0/execution-panel/.gitignore b/v3.0/execution-panel/.gitignore new file mode 100644 index 0000000..54bfd20 --- /dev/null +++ b/v3.0/execution-panel/.gitignore @@ -0,0 +1,42 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +# System Files +.DS_Store +Thumbs.db diff --git a/v3.0/execution-panel/README.md b/v3.0/execution-panel/README.md new file mode 100644 index 0000000..98e0467 --- /dev/null +++ b/v3.0/execution-panel/README.md @@ -0,0 +1,28 @@ +# BpsolClient + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). +Before running the tests make sure you are serving the app via `ng serve`. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/v3.0/execution-panel/e2e/app.e2e-spec.ts b/v3.0/execution-panel/e2e/app.e2e-spec.ts new file mode 100644 index 0000000..22eb0c4 --- /dev/null +++ b/v3.0/execution-panel/e2e/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { BpsolClientPage } from './app.po'; + +describe('bpsol-client App', () => { + let page: BpsolClientPage; + + beforeEach(() => { + page = new BpsolClientPage(); + }); + + it('should display message saying app works', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('app works!'); + }); +}); diff --git a/v3.0/execution-panel/e2e/app.po.ts b/v3.0/execution-panel/e2e/app.po.ts new file mode 100644 index 0000000..8f30397 --- /dev/null +++ b/v3.0/execution-panel/e2e/app.po.ts @@ -0,0 +1,11 @@ +import { browser, element, by } from 'protractor'; + +export class BpsolClientPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git a/v3.0/execution-panel/e2e/tsconfig.e2e.json b/v3.0/execution-panel/e2e/tsconfig.e2e.json new file mode 100644 index 0000000..ac7a373 --- /dev/null +++ b/v3.0/execution-panel/e2e/tsconfig.e2e.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "module": "commonjs", + "target": "es5", + "types":[ + "jasmine", + "node" + ] + } +} diff --git a/v3.0/execution-panel/karma.conf.js b/v3.0/execution-panel/karma.conf.js new file mode 100644 index 0000000..84b4cd5 --- /dev/null +++ b/v3.0/execution-panel/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/0.13/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular/cli'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular/cli/plugins/karma') + ], + client:{ + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + files: [ + { pattern: './src/test.ts', watched: false } + ], + preprocessors: { + './src/test.ts': ['@angular/cli'] + }, + mime: { + 'text/x-typescript': ['ts','tsx'] + }, + coverageIstanbulReporter: { + reports: [ 'html', 'lcovonly' ], + fixWebpackSourcePaths: true + }, + angularCli: { + environment: 'dev' + }, + reporters: config.angularCli && config.angularCli.codeCoverage + ? ['progress', 'coverage-istanbul'] + : ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; diff --git a/v3.0/execution-panel/package.json b/v3.0/execution-panel/package.json new file mode 100644 index 0000000..bb56eca --- /dev/null +++ b/v3.0/execution-panel/package.json @@ -0,0 +1,56 @@ +{ + "name": "bpsol-client", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/common": "^4.0.0", + "@angular/compiler": "^4.0.0", + "@angular/core": "^4.0.0", + "@angular/forms": "^4.0.0", + "@angular/http": "^4.0.0", + "@angular/platform-browser": "^4.0.0", + "@angular/platform-browser-dynamic": "^4.0.0", + "@angular/router": "^4.0.0", + "bootstrap": "^3.3.7", + "bpmn-js": "^0.20.5", + "bpmn-js-properties-panel": "^0.13.1", + "core-js": "^2.4.1", + "ejs-compiled-loader": "^1.1.0", + "jquery": "^3.2.1", + "md5": "^2.2.1", + "prismjs": "^1.6.0", + "rxjs": "^5.1.0", + "zone.js": "^0.8.4" + }, + "devDependencies": { + "@angular/cli": "1.0.0", + "@angular/compiler-cli": "^4.0.0", + "@types/jasmine": "2.5.38", + "@types/jquery": "^3.3.6", + "@types/node": "~6.0.60", + "codelyzer": "~2.0.0", + "jasmine-core": "~2.5.2", + "jasmine-spec-reporter": "~3.2.0", + "karma": "~1.4.1", + "karma-chrome-launcher": "~2.0.0", + "karma-cli": "~1.0.1", + "karma-coverage-istanbul-reporter": "^0.2.0", + "karma-jasmine": "~1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", + "less": "^2.7.2", + "less-loader": "^2.2.3", + "protractor": "~5.1.0", + "ts-node": "~2.0.0", + "tslint": "~4.5.0", + "typescript": "~2.2.0" + } +} diff --git a/v3.0/execution-panel/protractor.conf.js b/v3.0/execution-panel/protractor.conf.js new file mode 100644 index 0000000..1c5e1e5 --- /dev/null +++ b/v3.0/execution-panel/protractor.conf.js @@ -0,0 +1,30 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './e2e/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + beforeLaunch: function() { + require('ts-node').register({ + project: 'e2e/tsconfig.e2e.json' + }); + }, + onPrepare() { + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; diff --git a/v3.0/execution-panel/src/app/app.component.css b/v3.0/execution-panel/src/app/app.component.css new file mode 100644 index 0000000..c4bbb22 --- /dev/null +++ b/v3.0/execution-panel/src/app/app.component.css @@ -0,0 +1,11 @@ +.highlight:not(.djs-connection) .djs-visual > :nth-child(1) { + fill: green !important; /* color elements as green */ +} + +main { + padding: 1em; + font-family: Arial, Helvetica, sans-serif; + text-align: center; + /*margin-top: 50px;*/ + display: block; +} diff --git a/v3.0/execution-panel/src/app/app.component.html b/v3.0/execution-panel/src/app/app.component.html new file mode 100644 index 0000000..eeceb42 --- /dev/null +++ b/v3.0/execution-panel/src/app/app.component.html @@ -0,0 +1,2 @@ +
p { color: red }
+ diff --git a/v3.0/execution-panel/src/app/app.component.spec.ts b/v3.0/execution-panel/src/app/app.component.spec.ts new file mode 100644 index 0000000..c740bcd --- /dev/null +++ b/v3.0/execution-panel/src/app/app.component.spec.ts @@ -0,0 +1,32 @@ +import { TestBed, async } from '@angular/core/testing'; + +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + + it('should create the app', async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + })); + + it(`should have as title 'app works!'`, async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('app works!'); + })); + + it('should render title in a h1 tag', async(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('app works!'); + })); +}); diff --git a/v3.0/execution-panel/src/app/app.component.ts b/v3.0/execution-panel/src/app/app.component.ts new file mode 100644 index 0000000..9e6f55f --- /dev/null +++ b/v3.0/execution-panel/src/app/app.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: + '', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'app works!'; + code = + // " public class Proc { public int method() {} }" + 'pragma solidity ^0.4.0;\ncontract BPMNProc { function () returns () { return; }}'; +} diff --git a/v3.0/execution-panel/src/app/app.module.ts b/v3.0/execution-panel/src/app/app.module.ts new file mode 100644 index 0000000..195ef0f --- /dev/null +++ b/v3.0/execution-panel/src/app/app.module.ts @@ -0,0 +1,39 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { RouterModule, Routes } from '@angular/router'; + +import { AppComponent } from './app.component'; +import { ModelerComponent } from 'app/modeler/modeler.component'; +import { DashboardComponent } from 'app/dashboard/dashboard.component'; +import { ViewerComponent } from 'app/viewer/viewer.component'; +import { ProcessStorage } from 'app/data-store/data-store'; + +const routes: Routes = [ + { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, + { path: 'modeler', component: ModelerComponent }, + { path: 'viewer', component: ViewerComponent }, + { path: 'dashboard', component: DashboardComponent }, +]; + +@NgModule({ + declarations: [ + ModelerComponent, + DashboardComponent, + ViewerComponent, + AppComponent + ], + imports: [ + BrowserModule, + FormsModule, + HttpModule, + RouterModule.forRoot(routes) + ], + exports: [ + RouterModule + ], + providers: [ProcessStorage], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/v3.0/execution-panel/src/app/dashboard/dashboard.component.html b/v3.0/execution-panel/src/app/dashboard/dashboard.component.html new file mode 100644 index 0000000..fe2b19c --- /dev/null +++ b/v3.0/execution-panel/src/app/dashboard/dashboard.component.html @@ -0,0 +1,287 @@ + + +
+ +
+
+ +
+
+
+
+ +
+
+
+ + + + +
+
+
+
+
+
+ +
+ +
+
+
+
+
+ + + + +
+
+
+
+
+ + +
+
+
+
+
+
+ + + + + +
+
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
Nomination
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
Release
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
Vote
+
+
+ +
+
+ +
+
+ + + + +
+
+ + + + +
+
+ +
+
+
+
+
+
+ +
+ +
+ +
+ + +
+
+
+
+ +
+
+
+ + + + +
+
+
+
+
+
+
+
+ Process Models List => +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+

+ {{proc.name}} +
+ + + +
+

+
+
+
+
+
+ Solidity » +
+
+
+
+
+
+
+
+
+ Instances » +
+
+
{{instance}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + +
+
+
+ + + + diff --git a/v3.0/execution-panel/src/app/dashboard/dashboard.component.ts b/v3.0/execution-panel/src/app/dashboard/dashboard.component.ts new file mode 100644 index 0000000..46372c0 --- /dev/null +++ b/v3.0/execution-panel/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,178 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Http } from '@angular/http'; +import { element } from 'protractor'; +import { ProcessStorage } from '../data-store/data-store'; +import * as Viewer from 'bpmn-js/lib/Viewer'; + +declare function require(name: string); + +const jQuery = require('jquery'); +const Prism = require('prismjs'); + +@Component({ + selector: 'dashboard', + templateUrl: './dashboard.component.html' +}) +export class DashboardComponent { + toSearch = ''; + rootProcess = ''; + policyId = ''; + registryAddress = ''; + + bindingPolicy = ''; + + nominatorRole = ''; + nomineeRole = ''; + procesCase = ''; + + nominatorNAddress = ''; + nomineeNAddress = ''; + + endorserRole = ''; + endorserAddress = ''; + + nominatorRAddress = ''; + onNomination = "true"; + isAccepted = "true"; + + roleState = 'UNDEFINED'; + pCaseQuery = ''; + roleToQuery = ''; + caseCreator = ''; + caseCreatorRole = ''; + + + constructor(private router: Router, public processStorage: ProcessStorage) { + this.searchElement(); + } + + + findRoleState() { + let errorM = ''; + errorM = this.checkEmpty(this.pCaseQuery, '[Role] '); + if(errorM !== '') { + alert('Error: ' + errorM + 'cannot be empty'); + errorM = ''; + } else { + this.processStorage.findRoleState(this.roleToQuery, this.pCaseQuery); + this.roleState = this.processStorage.roleState; + } + } + + checkEmpty(input, text) { + return input == '' ? text : ''; + } + + createInstance(pId) { + let errorM = this.checkEmpty(this.caseCreator, '[Case Creator Address] '); + if(errorM !== '') { + alert('Error: ' + errorM + 'cannot be empty'); + errorM = ''; + } else { + this.processStorage.createInstance(pId, this.caseCreator, this.caseCreatorRole); + } + } + + nominate() { + let errorM = ''; + errorM = this.checkEmpty(this.nominatorRole, '[Nominator Role] ') + + this.checkEmpty(this.nomineeRole, '[Nominee Role] ') + + this.checkEmpty(this.procesCase, '[Process Case] ') + + this.checkEmpty(this.nominatorNAddress, '[Nominator Address] ') + + this.checkEmpty(this.nomineeNAddress, '[Nominee Address] '); + if(errorM !== '') { + alert('Error: ' + errorM + 'cannot be empty'); + errorM = ''; + } else { + console.log(this.nominatorRole); + this.processStorage.nominate(this.nominatorRole, this.nomineeRole, this.nominatorNAddress, this.nomineeNAddress, this.procesCase); + } + } + + release() { + let errorM = ''; + errorM = this.checkEmpty(this.nominatorRole, '[Nominator Role] ') + + this.checkEmpty(this.nomineeRole, '[Nominee Role] ') + + this.checkEmpty(this.procesCase, '[Process Case] ') + + this.checkEmpty(this.nominatorRAddress, '[Nominator Address] '); + + if(errorM !== '') { + alert('Error: ' + errorM + 'cannot be empty'); + errorM = ''; + } else { + this.processStorage.release(this.nominatorRole, this.nomineeRole, this.nominatorRAddress, this.procesCase); + } + } + + vote() { + let errorM = ''; + errorM = this.checkEmpty(this.nominatorRole, '[Nominator Role] ') + + this.checkEmpty(this.nomineeRole, '[Nominee Role] ') + + this.checkEmpty(this.procesCase, '[Process Case] ') + + this.checkEmpty(this.endorserRole, '[Endorser Role] ') + + this.checkEmpty(this.endorserAddress, '[Endorser Address] '); + + if(errorM !== '') { + alert('Error: ' + errorM + 'cannot be empty'); + errorM = ''; + } else { + this.processStorage.vote(this.nominatorRole, this.nomineeRole, this.endorserRole, this.endorserAddress, this.procesCase, this.onNomination === "true", this.isAccepted === "true"); + } + } + + openFile(event) { + const input = event.target; + var myFile = event.target.files[0]; + var reader = new FileReader(); + reader.readAsText(myFile); + reader.onload = () => { + this.bindingPolicy = reader.result.toString(); + + }; + } + + drawModel(proc: any) { + let viewer = new Viewer({ container: '#proc.id' + '_canvas' }); + let canvas = viewer.get('#proc.id' + '_canvas'); + viewer.importXML(proc.bpmn, (definitions) => { }) + } + + sendResourceModel() { + this.processStorage.resourceModel = this.bindingPolicy; + this.processStorage.sendResourceModel(); + } + + createProcessRegistry() { + this.processStorage.createProcessRegistry(); + } + + loadProcessRegistry() { + this.processStorage.loadProcessRegistry(this.registryAddress); + } + + openModeler() { + this.router.navigateByUrl('/modeler'); + } + openViewer(procName, instance) { + this.processStorage.modelId = procName; + this.processStorage.actInst = instance; + this.router.navigateByUrl('/viewer'); + } + + searchElement() { + this.processStorage.searchRegisteredModel(this.toSearch); + } + + createTaskRole() { + this.processStorage.createTaskRole(this.rootProcess, this.policyId); + } + + updateInstances(proc) { + this.processStorage.updateInstances(proc); + } + + getSolidity(proc) { + return Prism.highlight(proc.solidity, Prism.languages.javascript); + } +} diff --git a/v3.0/execution-panel/src/app/data-store/data-store.ts b/v3.0/execution-panel/src/app/data-store/data-store.ts new file mode 100644 index 0000000..a19a165 --- /dev/null +++ b/v3.0/execution-panel/src/app/data-store/data-store.ts @@ -0,0 +1,245 @@ +'use strict'; + +import { Callback } from '@ngtools/webpack/src/webpack'; +import { Injectable } from '@angular/core'; +import { Http, Headers } from '@angular/http'; + + +@Injectable() +export class ProcessStorage { + model = require('raw-loader!./initial.bpmn'); + resourceModel = '' + modelId = ''; + + processes = []; + + instances = {}; + + actInst: string; + + roleState: string; + + constructor(private http: Http) { } + + sendResourceModel() { + if (this.resourceModel.length < 1) + alert('Resource File cannot be empty. Be sure to upload the required file.'); + else { + this.http + .post('http://localhost:3000/resources/policy', { model: this.resourceModel }) + .subscribe( + (resp) => { + console.log(resp.json()); + }, + (error) => { + console.log(error); + }); + } + } + + createTaskRole(rootProcess, policyId) { + if (rootProcess.length < 1) + alert('Root proces Id cannot be empty.'); + else { + this.http + .post('http://localhost:3000/resources/task-role', { rootProc: rootProcess, policyId: policyId }) + .subscribe( + (resp) => { + console.log(resp.json()); + }, + (error) => { + console.log(error) + }); + } + } + + findRoleState(role, pCase) { + this.http + .get(`http://localhost:3000/resources/${role}/${pCase}`) + .subscribe( + (resp) => { + const resJ = resp.json(); + this.roleState = resp.json().state; + alert('Role State ' + this.roleState); + }, + (error) => { } + ); + + } + + nominate(rNominator, rNominee, nominator, nominee, pCase) { + this.http + .post('http://localhost:3000/resources/nominate', { rNominator: rNominator, rNominee: rNominee, nominator: nominator, nominee: nominee, pCase: pCase }) + .subscribe( + (resp) => { + console.log(resp.toString()); + }, + (error) => { + console.log(error) + }); + } + + release(rNominator, rNominee, nominator, pCase) { + this.http + .post('http://localhost:3000/resources/release', {rNominator: rNominator, rNominee: rNominee, nominator: nominator, pCase: pCase }) + .subscribe( + (resp) => { + console.log(resp.toString()); + }, + (error) => { + console.log(error) + }); +} + +vote(rNominator, rNominee, rEndorser, endorser, pCase, onNomination, isAccepted) { + this.http + .post('http://localhost:3000/resources/vote', {rNominator: rNominator, rNominee: rNominee, rEndorser: rEndorser, endorser: endorser, pCase: pCase, onNomination: onNomination, isAccepted: isAccepted }) + .subscribe( + (resp) => { + console.log(resp.toString()); + }, + (error) => { + console.log(error) + }); +} + + createProcessRegistry() { + this.http + .post('http://localhost:3000/registry', { }) + .subscribe( + (resp) => { + console.log('SUCCESS: ', resp.json()); + return resp.json().address; + }, + (error) => { + console.log('ERROR: ', error); + }); + } + + loadProcessRegistry(registryAddress: string) { + this.http + .post('http://localhost:3000/registry/load', {from: registryAddress}) + .subscribe( + (resp) => { + console.log('SUCCESS: ', resp.json()); + }, + (error) => { + console.log('ERROR: ', error); + }); + + } + + + registerModel(model: string) { + this.http + .post('http://localhost:3000/interpreter/models', { bpmn: model }) + .subscribe( + (resp) => { + const res = resp.json(); + if (res.id && res.bpmn && res.solidity && this.processes.indexOf(res) < 0) { + this.searchRegisteredModel(''); + console.log('Model ' + res.name + ' succesfully registered'); + } else { + console.log('Error trying to register ' + this.modelId); + } + }, + (error) => { }); + } + + searchRegisteredModel(modelId: string) { + this.http + .get('http://localhost:3000/models') + .subscribe( + (resp) => { + const resJ = resp.json(); + if (modelId === '') { + this.processes = resJ; + } else { + this.processes = []; + resJ.forEach(element => { + if ((element.name.indexOf(modelId) >= 0 || element.id.indexOf(modelId) >= 0) && this.processes.indexOf(element) < 0) { + this.processes.push(element); + } + }); + } + }, + (error) => { } + ); + } + + updateModels() { + this.http.get('http://localhost:3000/models') + .subscribe(resp => { + this.processes = []; + resp.json().forEach(element => { + if (this.processes.indexOf(element) < 0) { + this.processes.push(element); + } + }); + }); + } + + createInstance(procId: string, caseCreator: string, creatorRole: string) { + this.http.post('http://localhost:3000/models/' + procId, {caseCreator: caseCreator, creatorRole : creatorRole}) + .subscribe(resp => { + const res = resp.json(); + if (!this.instances[procId]) { + this.instances[procId] = []; + } + this.instances[procId].push(res.address); + }); + }; + + updateInstances(procId: string) { + this.http.get('http://localhost:3000/processes/') + .subscribe(resp => { + const res = resp.json(); + this.instances[procId] = []; + res.forEach(element => { + if (element.id === procId) { + this.instances[procId].push(element.address); + } + }); + return this.instances; + }, + (error) => { + console.log('Error ', error) + return []; }); + return []; + } + + resetModel() { + this.updateModels(); + this.model = require('raw-loader!./initial.bpmn'); + this.updateName(); + }; + + updateName() { + this.modelId = 'Process_' + this.processes.length; + this.model = this.model.replace('id="Process"', 'id="' + this.modelId + '"'); + this.model = this.model.replace('name="Process"', 'name="' + this.modelId + '"'); + } + + hasModel(procId: string) { + for (let i = 0; i < this.processes.length; i++) { + if (this.processes[i].id === procId) { return true; } + } + return false; + } + + get Model(): string { return this.model; } + + set Model(nModel: string) { this.model = nModel; } + + get ModelId(): string { return this.modelId; } + + set ModelId(nName: string) { this.modelId = nName; } + + get ActInst() { return this.actInst; } + + set ActInst(nActInst: string) { this.actInst = nActInst; } + + get Models(): any[] { return this.processes; } + + getInstance(name: string) { return this.instances[name] ? this.instances[name] : []; } +} diff --git a/v3.0/execution-panel/src/app/data-store/initial.bpmn b/v3.0/execution-panel/src/app/data-store/initial.bpmn new file mode 100644 index 0000000..321b3df --- /dev/null +++ b/v3.0/execution-panel/src/app/data-store/initial.bpmn @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/v3.0/execution-panel/src/app/modeler/modeler.component.css b/v3.0/execution-panel/src/app/modeler/modeler.component.css new file mode 100644 index 0000000..98b028b --- /dev/null +++ b/v3.0/execution-panel/src/app/modeler/modeler.component.css @@ -0,0 +1,17 @@ +#canvas { + width: 100%; + height: 800px; +} +#js-properties-panel { + position: absolute; + top: 0; + bottom: 0; + right: 0; + width: 260px; + z-index: 10; + border-left: 1px solid #ccc; + overflow: auto; +} + +@import '~bpmn-js/assets/bpmn-font/css/bpmn.css'; + diff --git a/v3.0/execution-panel/src/app/modeler/modeler.component.html b/v3.0/execution-panel/src/app/modeler/modeler.component.html new file mode 100644 index 0000000..de3ade6 --- /dev/null +++ b/v3.0/execution-panel/src/app/modeler/modeler.component.html @@ -0,0 +1,27 @@ +
+
+ + + +
+
+ +
+
diff --git a/v3.0/execution-panel/src/app/modeler/modeler.component.ts b/v3.0/execution-panel/src/app/modeler/modeler.component.ts new file mode 100644 index 0000000..e933b8f --- /dev/null +++ b/v3.0/execution-panel/src/app/modeler/modeler.component.ts @@ -0,0 +1,97 @@ +import { Component, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; +import { Http } from "@angular/http"; +import * as Modeler from "bpmn-js/lib/Modeler.js"; +import * as propertiesPanelModule from "bpmn-js-properties-panel"; +import * as propertiesProviderModule from "bpmn-js-properties-panel/lib/provider/bpmn"; +import { ProcessStorage } from "app/data-store/data-store"; + +import { PaletteProvider } from "./palette"; +import { CustomPropertiesProvider } from "./props-provider"; + +const fs = require("fs"); +const async = require("async"); + +const customPaletteModule = { + paletteProvider: ["type", PaletteProvider] +}; + +const customPropertiesProviderModule = { + __init__: ["propertiesProvider"], + propertiesProvider: ["type", CustomPropertiesProvider] +}; + +@Component({ + selector: "modeler", + styleUrls: ["./modeler.component.css"], + templateUrl: "./modeler.component.html" +}) +export class ModelerComponent implements OnInit { + modeler: any; + + modelText: string; + + constructor(private router: Router, private processStorage: ProcessStorage) {} + + ngOnInit() { + this.processStorage.resetModel(); + this.modeler = new Modeler({ + container: '#canvas', + additionalModules: [ + propertiesPanelModule, + propertiesProviderModule, + customPropertiesProviderModule, + customPaletteModule + ], + propertiesPanel: { + parent: '#js-properties-panel' + } + }); + this.modeler.importXML( + this.processStorage.model, + (error, definitions) => {} + ); + } + + openFile(event) { + const input = event.target; + for (let index = 0; index < input.files.length; index++) { + const reader = new FileReader(); + reader.onload = () => { + this.processStorage.model = reader.result; + this.modeler.importXML( + this.processStorage.model, + (error, definitions) => {} + ); + }; + reader.readAsText(input.files[index]); + } + } + + validateName() { + this.modeler.saveXML({ format: true }, (err: any, xml: string) => { + for (let i = 0; i < this.modeler.definitions.rootElements.length; i++) { + if (this.modeler.definitions.rootElements[i].$type === 'bpmn:Process') { + if (this.processStorage.hasModel(this.modeler.definitions.rootElements[i].id)) { + this.modelText = + 'The selected ID exists on the Workspace. Please, change this value and try again.'; + } else if (!this.modeler.definitions.rootElements[i].name || this.modeler.definitions.rootElements[i].name === '') { + this.modelText = + 'The Name of the model cannot be empty. Please, update this value and try again.'; + } else { + this.goToDashborad(); + this.processStorage.modelId = this.modeler.definitions.rootElements[i].id; + this.processStorage.registerModel(xml); + this.modelText = + 'Working in Model Registration. Please, take into account that this may require some seconds.'; + } + break; + } + } + }); + } + + goToDashborad() { + this.router.navigateByUrl('/dashboard'); + } +} diff --git a/v3.0/execution-panel/src/app/modeler/palette.ts b/v3.0/execution-panel/src/app/modeler/palette.ts new file mode 100644 index 0000000..2a2b521 --- /dev/null +++ b/v3.0/execution-panel/src/app/modeler/palette.ts @@ -0,0 +1,105 @@ +export function PaletteProvider(palette: any, create: any, elementFactory: any, spaceTool: any, lassoTool: any) { + this._create = create; + this._elementFactory = elementFactory; + this._spaceTool = spaceTool; + this._lassoTool = lassoTool; + palette.registerProvider(this); +} + +const DiagramList: any = {}; +const diagramSubject: any = {}; + +PaletteProvider['$inject'] = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool']; + +PaletteProvider.prototype.getPaletteEntries = function (element: any) { + var self = this; + + var actions = {}, + create = this._create, + elementFactory = this._elementFactory, + spaceTool = this._spaceTool, + lassoTool = this._lassoTool; + + + function createAction(type: string, group: string, className: string, title: string = null, options: any = {}) { + function createListener(event: any) { + var shape = elementFactory.createShape(Object.assign({type: type}, options)); + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded; + } + create.start(event, shape); + } + + return { + group: group, + className: className, + title: title || 'Create ' + type.replace(/^bpmn\:/, ''), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + + function createParticipant(event, collapsed) { + create.start(event, elementFactory.createParticipantShape(collapsed)); + } + + Object.assign(actions, { + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: 'Activate the lasso tool', + action: { + click: function(event) { + lassoTool.activateSelection(event); + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: 'Activate the create/remove space tool', + action: { + click: function(event) { + spaceTool.activateSelection(event); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction( + 'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none' + ), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none' + ), + 'create.end-event': createAction( + 'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none' + ), + 'create.exclusive-gateway': createAction( + 'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-xor' + ), + 'create.task': createAction( + 'bpmn:Task', 'activity', 'bpmn-icon-task' + ), + 'create.subprocess-expanded': createAction( + 'bpmn:SubProcess', 'activity', 'bpmn-icon-subprocess-expanded', 'Create expanded SubProcess', + { isExpanded: true } + ), + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: 'Create Pool/Participant', + action: { + dragstart: createParticipant, + click: createParticipant + } + } + }); + + return actions; +}; diff --git a/v3.0/execution-panel/src/app/modeler/props-provider.ts b/v3.0/execution-panel/src/app/modeler/props-provider.ts new file mode 100644 index 0000000..e6a9973 --- /dev/null +++ b/v3.0/execution-panel/src/app/modeler/props-provider.ts @@ -0,0 +1,81 @@ +declare function require(name: string); + +const inherits = require('inherits'); + +var PropertiesActivator = require('bpmn-js-properties-panel/lib/PropertiesActivator'); + +// Require all properties you need from existing providers. +// In this case all available bpmn relevant properties without camunda extensions. +var processProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/ProcessProps'), + eventProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/EventProps'), + linkProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/LinkProps'), + documentationProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/DocumentationProps'), + idProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/IdProps'), + nameProps = require('bpmn-js-properties-panel/lib/provider/bpmn/parts/NameProps'), + sequenceFlowProps = require('bpmn-js-properties-panel/lib/provider/camunda/parts/SequenceFlowProps'), + scriptTaskProps = require('bpmn-js-properties-panel/lib/provider/camunda/parts/ScriptTaskProps'), + userTaskProps = require('bpmn-js-properties-panel/lib/provider/camunda/parts/UserTaskProps'), + serviceTaskDelegateProps = require('bpmn-js-properties-panel/lib/provider/camunda/parts/ServiceTaskDelegateProps'), + multiInstanceLoopProps = require('bpmn-js-properties-panel/lib/provider/camunda/parts/MultiInstanceLoopProps'); + +// The general tab contains all bpmn relevant properties. +// The properties are organized in groups. +function createGeneralTabGroups(element: any, bpmnFactory: any, elementRegistry: any) { + + const generalGroup = { + id: 'general', + label: 'General', + entries: new Array, + }; + + idProps(generalGroup, element, elementRegistry); + nameProps(generalGroup, element); + processProps(generalGroup, element); + + const detailsGroup = { + id: 'details', + label: 'Details', + entries: new Array, + }; + linkProps(detailsGroup, element); + eventProps(detailsGroup, element, bpmnFactory, elementRegistry); + sequenceFlowProps(detailsGroup, element, bpmnFactory, elementRegistry); + scriptTaskProps(detailsGroup, element, bpmnFactory, elementRegistry); + userTaskProps(detailsGroup, element, bpmnFactory, elementRegistry); + serviceTaskDelegateProps(detailsGroup, element, bpmnFactory, elementRegistry); + multiInstanceLoopProps(detailsGroup, element, bpmnFactory, elementRegistry); + + const documentationGroup = { + id: 'documentation', + label: 'Documentation', + entries: new Array, + }; + + documentationProps(documentationGroup, element, bpmnFactory); + + return [ + generalGroup, + detailsGroup, + documentationGroup + ]; +} + +export function CustomPropertiesProvider(eventBus: any, bpmnFactory: any, elementRegistry: any) { + + PropertiesActivator.call(this, eventBus); + + this.getTabs = function (element: any) { + + const generalTab = { + id: 'general', + label: 'General', + groups: createGeneralTabGroups(element, bpmnFactory, elementRegistry) + }; + + return [ + generalTab + ]; + }; +} + +inherits(CustomPropertiesProvider, PropertiesActivator); diff --git a/v3.0/execution-panel/src/app/viewer/href-list-as-dropdown-menu.ejs b/v3.0/execution-panel/src/app/viewer/href-list-as-dropdown-menu.ejs new file mode 100644 index 0000000..84b7c91 --- /dev/null +++ b/v3.0/execution-panel/src/app/viewer/href-list-as-dropdown-menu.ejs @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/v3.0/execution-panel/src/app/viewer/input-params-as-form.ejs b/v3.0/execution-panel/src/app/viewer/input-params-as-form.ejs new file mode 100644 index 0000000..e5de66d --- /dev/null +++ b/v3.0/execution-panel/src/app/viewer/input-params-as-form.ejs @@ -0,0 +1,51 @@ +
+ + + +<% if(pCases.length > 1) { %> +
+ + +<% } %> +
+ + + +
+
+ <% if(inputs.length == 0 ) { %> +

NO INPUT DATA REQUIRED

+ <% } else { inputs.forEach( function (input) { + if (input.type.match(/^uint\d*$/)) { %> +
+ + +
+ <% } else if (input.type === 'bool') { %> +
+ + +
+ <% } else { %> +
+ + +
+ <% }}) } %> +
+
+
+ +
+ + + + + +
diff --git a/v3.0/execution-panel/src/app/viewer/viewer.component.css b/v3.0/execution-panel/src/app/viewer/viewer.component.css new file mode 100644 index 0000000..6592bed --- /dev/null +++ b/v3.0/execution-panel/src/app/viewer/viewer.component.css @@ -0,0 +1,4 @@ +#canvas { + width: 100%; + height: 1000px; +} diff --git a/v3.0/execution-panel/src/app/viewer/viewer.component.html b/v3.0/execution-panel/src/app/viewer/viewer.component.html new file mode 100644 index 0000000..6577a6b --- /dev/null +++ b/v3.0/execution-panel/src/app/viewer/viewer.component.html @@ -0,0 +1,6 @@ + + + +
diff --git a/v3.0/execution-panel/src/app/viewer/viewer.component.ts b/v3.0/execution-panel/src/app/viewer/viewer.component.ts new file mode 100644 index 0000000..4ea6c61 --- /dev/null +++ b/v3.0/execution-panel/src/app/viewer/viewer.component.ts @@ -0,0 +1,237 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Http } from '@angular/http'; +import * as Viewer from 'bpmn-js/lib/Viewer'; + +/////////////////// Start /////////////////////// +import { Observable } from 'rxjs/Observable'; +import { element } from 'protractor'; +import { ProcessStorage } from '../data-store/data-store'; +/////////////////// End ///////////////////////// + +declare function require(name: string); +const jQuery = require('jquery'); +const input_params_as_form = require('ejs-compiled-loader!./input-params-as-form.ejs'); +const href_list_as_dropdown_menu = require('ejs-compiled-loader!./href-list-as-dropdown-menu.ejs'); + +@Component({ + selector: 'viewer', + styleUrls: ['./viewer.component.css'], + templateUrl: "./viewer.component.html" +}) +export class ViewerComponent implements OnInit { + activeContracts = ['No Contracts Available']; + url = 'No Contracts Available'; + viewer: any; + canvas: any; + previousState: any = null; + selectedAddress = ''; + pCases = []; + + ///////////////////// Start ////////////////////////// + + connection; + message; + + //private sURL = 'http://localhost:8090'; + //private socket; + + ////////////////////// End ///////////////////////// + + constructor(private router: Router, private http: Http, private processStorage: ProcessStorage) { + const instances = processStorage.getInstance(processStorage.modelId); + this.activeContracts = []; + instances.forEach(element => { + this.activeContracts.push('http://localhost:3000/processes/' + element); + }); + if (this.activeContracts.length === 0) { + this.activeContracts.push('No Contracts Available'); + this.url = 'No Contracts Available'; + } else { + this.url = 'http://localhost:3000/processes/' + processStorage.actInst; + } + } + + loadModel() { + if (this.url !== 'No Contracts Available' && this.url !== '') { + this.http.get(this.url) + .subscribe(resp => + this.viewer.importXML(resp.json().bpmn, (definitions) => { + this.renderState(resp.json()); + }) + ); + } + } + + goToDashborad() { + this.router.navigateByUrl('/dashboard'); + } + + updateContracts() { + this.processStorage.updateInstances(this.processStorage.modelId); + const res = this.processStorage.getInstance(this.processStorage.modelId); + this.activeContracts = ['No Contracts Available']; + res.forEach(element => { + this.url = 'http://localhost:3000/processes/' + element; + this.activeContracts.push(this.url); + }); + if (this.activeContracts.length > 1 && this.activeContracts[0] === 'No Contracts Available') { + this.activeContracts.splice(0, 1); + } + } + + renderState(state: any) { + if (this.previousState) { + this.previousState.workitems.forEach(workItem => { + try { + this.canvas.removeMarker(workItem.elementId, 'highlight'); + this.canvas.removeMarker(workItem.elementId, 'highlight-running'); + } catch(Error) {} + }); + } + this.previousState = []; + state.workitems.forEach(workItem => { + try { this.canvas.addMarker(workItem.elementId, 'highlight'); } + catch(Error) {} + }); + this.previousState = state; + } + + setupListeners() { + const eventBus = this.viewer.get('eventBus'); + const overlays = this.viewer.get('overlays'); + eventBus.on('element.click', (e: any) => { + let nodeId = e.element.id; + let workItem = undefined; + if (this.previousState) { + this.previousState.workitems.forEach(workItem1 => { + if (workItem1.elementId === e.element.id) { + workItem = workItem1; + nodeId = e.element.id; + } + }); + } + if (workItem) { + this.canvas.removeMarker(workItem.elementId, 'highlight'); + this.canvas.addMarker(workItem.elementId, 'highlight-running'); + let inputParam = [] + if (workItem.input.length > 0) + inputParam = workItem.input; + this.pCases = workItem.pCases; + const overlayHtml = jQuery(input_params_as_form({ nodeId: workItem.elementId, inputs: workItem.input, pCases: workItem.pCases, href: workItem.hrefs })); + + overlays.add(workItem.elementId, { position: { bottom: 0, right: 0 }, html: overlayHtml }); + overlayHtml + .find(`#${workItem.elementId}_save`) + .click((e: any) => { + const nodeId1 = e.target.id.slice(0, e.target.id.indexOf('_save')); + overlays.remove({ element: nodeId1 }); + + const children = e.target.parentElement.querySelectorAll('.form-control'); + const tabs = e.target.parentElement.querySelectorAll('.active'); + + // Finding the operation to perform on the WorkItem: Execution, Allocation, Revocation + let operation = 0; + let values: Array = []; + tabs.forEach((child: any) => { + if (child.id === 'home') { + console.log('EXECUTION OPERATION '); + operation = 0; + workItem.input.forEach((input: any) => { + children.forEach((child: any) => { + if (child.id === input.name) { + values.push(input.type === 'bool' ? child.checked : child.value); + } + }); + }); + } + }); + + + let workItemIndex = 0; + + // Finding the user who is performing the operation + let user_address = ''; + let caseAddress = 0; + + children.forEach((child: any) => { + if(child.id === 'pCase') { + workItemIndex = child.value; + } + if (child.id === 'user_address') { + user_address = child.value; + } + }); + this.http.post('http://localhost:3000' + workItem.hrefs[workItemIndex], { elementId: workItem.elementId, inputParameters: values, user: user_address}) + .subscribe(resp => this.http.get(this.url).subscribe(resp1 => this.renderState(resp1.json()))); + }); + overlayHtml.find(`#${workItem.elementId}_cancel`).click((e1: any) => { + overlays.remove({ element: workItem.elementId }); + }); + } + /* + this.previousState.externalItemGroupList.forEach(externalItemGroup => { + if (externalItemGroup.elementId === nodeId) { + const toDisplay = []; + for (let i = 0; i < externalItemGroup.status.length; i++) { + toDisplay.push(externalItemGroup.hrefs[i]); + } + if (toDisplay.length > 0) { + const overlayHtml = jQuery(href_list_as_dropdown_menu({ nodeId: nodeId, hrefList: toDisplay })); + overlays.add(nodeId, { position: { bottom: 0, right: 0 }, html: overlayHtml }); + overlayHtml.click((e1: any) => { + // this.processStorage.modelId = e.element.businessObject.name; + // this.processStorage.actInst = e1.target.innerText; + // this.router.navigateByUrl('/viewer'); + }); + overlayHtml.toggle(); + } + } + }); */ + }); + } + + ngOnInit(): void { + this.viewer = new Viewer({ container: '#canvas' }); + this.canvas = this.viewer.get('canvas'); + this.updateContracts(); + this.setupListeners(); + + /////////////////////////// Start ////////////////////////////////////// +/* + this.connection = this.getMessages().subscribe(message => { + this.loadModel(); + console.log('Message from Server ...'); + }); +*/ + //////////////////////////// End ////////////////////////////////////// + + } + + ////////////////////////// Start /////////////////////////// + +/* + getMessages() { + const observable = new Observable(observer => { + this.socket = io(this.sURL); + this.socket.on('message', (data) => { + observer.next(data); + }); + return () => { + this.socket.disconnect(); + }; + }); + return observable; + } +*/ + + // tslint:disable-next-line:use-life-cycle-interface + ngOnDestroy() { + + } + + + + /////////////////////// End ////////////////////////////// + +} diff --git a/v3.0/execution-panel/src/assets/.gitkeep b/v3.0/execution-panel/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/v3.0/execution-panel/src/environments/environment.prod.ts b/v3.0/execution-panel/src/environments/environment.prod.ts new file mode 100644 index 0000000..3612073 --- /dev/null +++ b/v3.0/execution-panel/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/v3.0/execution-panel/src/environments/environment.ts b/v3.0/execution-panel/src/environments/environment.ts new file mode 100644 index 0000000..b7f639a --- /dev/null +++ b/v3.0/execution-panel/src/environments/environment.ts @@ -0,0 +1,8 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `.angular-cli.json`. + +export const environment = { + production: false +}; diff --git a/v3.0/execution-panel/src/favicon.ico b/v3.0/execution-panel/src/favicon.ico new file mode 100644 index 0000000..8081c7c Binary files /dev/null and b/v3.0/execution-panel/src/favicon.ico differ diff --git a/v3.0/execution-panel/src/index.html b/v3.0/execution-panel/src/index.html new file mode 100644 index 0000000..36613ed --- /dev/null +++ b/v3.0/execution-panel/src/index.html @@ -0,0 +1,14 @@ + + + + + BpsolClient + + + + + + + Loading... + + diff --git a/v3.0/execution-panel/src/main.ts b/v3.0/execution-panel/src/main.ts new file mode 100644 index 0000000..a6fec64 --- /dev/null +++ b/v3.0/execution-panel/src/main.ts @@ -0,0 +1,15 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +import 'bpmn-js/assets/bpmn-font/css/bpmn.css'; +import 'bpmn-js/assets/bpmn-font/css/bpmn-embedded.css'; +import 'diagram-js/assets/diagram-js.css'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/v3.0/execution-panel/src/polyfills.ts b/v3.0/execution-panel/src/polyfills.ts new file mode 100644 index 0000000..53bdaf1 --- /dev/null +++ b/v3.0/execution-panel/src/polyfills.ts @@ -0,0 +1,68 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following to support `@angular/animation`. */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + + +/** Evergreen browsers require these. **/ +import 'core-js/es6/reflect'; +import 'core-js/es7/reflect'; + + +/** ALL Firefox browsers require the following to support `@angular/animation`. **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + + + +/*************************************************************************************************** + * Zone JS is required by Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ + +/** + * Date, currency, decimal and percent pipes. + * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 + */ +// import 'intl'; // Run `npm install --save intl`. diff --git a/v3.0/execution-panel/src/styles.css b/v3.0/execution-panel/src/styles.css new file mode 100644 index 0000000..dec020b --- /dev/null +++ b/v3.0/execution-panel/src/styles.css @@ -0,0 +1,15 @@ +/* You can add global styles to this file, and also import other style files */ +.highlight:not(.djs-connection) .djs-visual > :nth-child(1) { + fill: green !important; /* color elements as green */ +} +.highlight-running:not(.djs-connection) .djs-visual > :nth-child(1) { + fill: lightgreen !important; /* color elements as green */ +} +.highlight-external:not(.djs-connection) .djs-visual > :nth-child(1) { + fill: sandybrown !important; /* color elements as green */ +} + +div.input-params { + min-width: 400px; +} + diff --git a/v3.0/execution-panel/src/test.ts b/v3.0/execution-panel/src/test.ts new file mode 100644 index 0000000..9bf7226 --- /dev/null +++ b/v3.0/execution-panel/src/test.ts @@ -0,0 +1,32 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare var __karma__: any; +declare var require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/v3.0/execution-panel/src/tsconfig.app.json b/v3.0/execution-panel/src/tsconfig.app.json new file mode 100644 index 0000000..5e2507d --- /dev/null +++ b/v3.0/execution-panel/src/tsconfig.app.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "module": "es2015", + "baseUrl": "", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git a/v3.0/execution-panel/src/tsconfig.spec.json b/v3.0/execution-panel/src/tsconfig.spec.json new file mode 100644 index 0000000..510e3f1 --- /dev/null +++ b/v3.0/execution-panel/src/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "module": "commonjs", + "target": "es5", + "baseUrl": "", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/v3.0/execution-panel/src/typings.d.ts b/v3.0/execution-panel/src/typings.d.ts new file mode 100644 index 0000000..ef5c7bd --- /dev/null +++ b/v3.0/execution-panel/src/typings.d.ts @@ -0,0 +1,5 @@ +/* SystemJS module definition */ +declare var module: NodeModule; +interface NodeModule { + id: string; +} diff --git a/v3.0/execution-panel/tsconfig.json b/v3.0/execution-panel/tsconfig.json new file mode 100644 index 0000000..a35a8ee --- /dev/null +++ b/v3.0/execution-panel/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "baseUrl": "src", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2016", + "dom" + ] + } +} diff --git a/v3.0/execution-panel/tslint.json b/v3.0/execution-panel/tslint.json new file mode 100644 index 0000000..9113f13 --- /dev/null +++ b/v3.0/execution-panel/tslint.json @@ -0,0 +1,116 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "import-blacklist": [true, "rxjs"], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [true, "ignore-params"], + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "typeof-compare": true, + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector": [true, "attribute", "app", "camelCase"], + "component-selector": [true, "element", "app", "kebab-case"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "no-access-missing-member": true, + "templates-use-public": true, + "invoke-injectable": true + } +}