├── .gitignore ├── README.md ├── javascript ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── app.js ├── enrollAdmin.js ├── files │ └── index.html ├── invoke.js ├── package-lock.json ├── package.json ├── query.js ├── registerUser.js └── run.sh ├── kill.sh └── startFabric.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /package-lock.json 3 | /hfc-key-store/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Z-Voting-API 2 | 3 | This is the NodeJS powered REST API that acts as the intermediarry between the HyperLedger Fabric network and the client. 4 | 5 | --- 6 | ## Requirements 7 | 8 | For development, you will only need Node.js and a node global package, Yarn, installed in your environement. 9 | 10 | ### Node 11 | - #### Node installation on Windows 12 | 13 | Just go on [official Node.js website](https://nodejs.org/) and download the installer. 14 | Also, be sure to have `git` available in your PATH, `npm` might need it (You can find git [here](https://git-scm.com/)). 15 | 16 | - #### Node installation on Ubuntu 17 | 18 | You can install nodejs and npm easily with apt install, just run the following commands. 19 | 20 | $ sudo apt install nodejs 21 | $ sudo apt install npm 22 | 23 | - #### Other Operating Systems 24 | You can find more information about the installation on the [official Node.js website](https://nodejs.org/) and the [official NPM website](https://npmjs.org/). 25 | 26 | If the installation was successful, you should be able to run the following command. 27 | 28 | $ node --version 29 | v8.11.3 30 | 31 | $ npm --version 32 | 6.1.0 33 | 34 | If you need to update `npm`, you can make it using `npm`! Cool right? After running the following command, just open again the command line and be happy. 35 | 36 | $ npm install npm -g 37 | 38 | --- 39 | 40 | ## Install 41 | 42 | $ git clone https://github.com/Z-Voting/Z-Voting-API 43 | $ cd Z-Voting-API/javascript 44 | $ npm install 45 | 46 | ## Running the project 47 | 48 | $ bash ./run.sh 49 | -------------------------------------------------------------------------------- /javascript/.editorconfig: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 4 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /javascript/.eslintignore: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | coverage 6 | -------------------------------------------------------------------------------- /javascript/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | module.exports = { 6 | env: { 7 | node: true, 8 | mocha: true 9 | }, 10 | parserOptions: { 11 | ecmaVersion: 8, 12 | sourceType: 'script' 13 | }, 14 | extends: "eslint:recommended", 15 | rules: { 16 | indent: ['error', 4], 17 | 'linebreak-style': ['error', 'unix'], 18 | quotes: ['error', 'single'], 19 | semi: ['error', 'always'], 20 | 'no-unused-vars': ['error', { args: 'none' }], 21 | 'no-console': 'off', 22 | curly: 'error', 23 | eqeqeq: 'error', 24 | 'no-throw-literal': 'error', 25 | strict: 'error', 26 | 'no-var': 'error', 27 | 'dot-notation': 'error', 28 | 'no-tabs': 'error', 29 | 'no-trailing-spaces': 'error', 30 | 'no-use-before-define': 'error', 31 | 'no-useless-call': 'error', 32 | 'no-with': 'error', 33 | 'operator-linebreak': 'error', 34 | yoda: 'error', 35 | 'quote-props': ['error', 'as-needed'] 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /javascript/.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless 78 | 79 | wallet 80 | !wallet/.gitkeep 81 | -------------------------------------------------------------------------------- /javascript/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | 'use strict'; 6 | 'use esversion: 6'; 7 | 8 | const { FileSystemWallet, Gateway } = require('fabric-network'); 9 | const path = require('path'); 10 | 11 | const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json'); 12 | 13 | function makeid(length) { 14 | let result = ''; 15 | let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 16 | let charactersLength = characters.length; 17 | for (let i = 0; i < length; i++) { 18 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 19 | } 20 | return result; 21 | } 22 | 23 | function concat() { 24 | let ret = ''; 25 | for (let i = 0; i < arguments.length; i++) { 26 | ret += ' ' + arguments[i]; 27 | } 28 | return ret; 29 | } 30 | 31 | async function main() { 32 | try { 33 | 34 | // Create a new file system based wallet for managing identities. 35 | const walletPath = path.join(process.cwd(), 'wallet'); 36 | const wallet = new FileSystemWallet(walletPath); 37 | console.log(`Wallet path: ${walletPath}`); 38 | 39 | // Check to see if we've already enrolled the user. 40 | const userExists = await wallet.exists('user1'); 41 | if (!userExists) { 42 | console.log('An identity for the user "user1" does not exist in the wallet'); 43 | console.log('Run the registerUserHandler.js application before retrying'); 44 | return; 45 | } 46 | 47 | // Create a new gateway for connecting to our peer node. 48 | const gateway = new Gateway(); 49 | await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } }); 50 | 51 | // Get the network (channel) our contract is deployed to. 52 | const network = await gateway.getNetwork('mychannel'); 53 | 54 | // Get the contract from the network. 55 | const contract = network.getContract('fabcar'); 56 | 57 | ////////////////////////////////////////////////// 58 | //////////////////////////////////// putting a boundary to keep new code seperate from old code 59 | let express = require('express'); 60 | let app = express(); 61 | let bodyParser = require('body-parser'); 62 | const PORT = process.env.PORT || 3000; 63 | 64 | // Create application/x-www-form-urlencoded parser 65 | 66 | // parse application/x-www-form-urlencoded 67 | app.use(bodyParser.urlencoded({ extended: false })); 68 | 69 | // parse application/json 70 | app.use(bodyParser.json()); 71 | 72 | 73 | app.use(express.static('files')); //we'll put the files in the "files" folder 74 | 75 | app.get('/getRandom', async (req, res) => { 76 | let randomData = {}; 77 | let seed = Date.now().toString(); 78 | let result = await contract.evaluateTransaction('getRandom', seed); 79 | result = JSON.parse(result); 80 | randomData.data = result; 81 | res.setHeader('content-type', 'text/json'); 82 | res.send(randomData); 83 | }); 84 | 85 | app.post('/getRandom', async (req, res) => { 86 | let seed = req.body.seed.toString(); 87 | let result = await contract.evaluateTransaction('getRandom', seed); 88 | // result = JSON.parse(result); 89 | res.setHeader('content-type', 'text/json'); 90 | res.send(result); 91 | }); 92 | 93 | app.get('/generateUID', async (req, res) => { 94 | let result = await contract.evaluateTransaction('generateUID'); 95 | // result = JSON.parse(result); 96 | res.setHeader('content-type', 'text/json'); 97 | res.send(result); 98 | }); 99 | 100 | app.post('/generateUID', async (req, res) => { 101 | let result = await contract.evaluateTransaction('generateUID'); 102 | // result = JSON.parse(result); 103 | res.setHeader('content-type', 'text/json'); 104 | res.send(result); 105 | }); 106 | 107 | app.post('/createElection', async (req, res) => { 108 | let electionName = req.body.electionName.toString(); 109 | let electionDuration = req.body.electionDuration.toString(); 110 | 111 | // console.log(electionName, electionDuration); 112 | // res.setHeader('content-type', 'text/json'); 113 | 114 | let promise = contract.submitTransaction('createElection', electionName, electionDuration); 115 | promise.then((data) => { 116 | console.log(data); 117 | 118 | let msg = { 119 | status: 'success', 120 | message: 'Election Created' 121 | }; 122 | 123 | res.setHeader('content-type', 'text/json'); 124 | res.send(msg); 125 | }).catch((err) => { 126 | console.log(err); 127 | 128 | let msg = { 129 | status: 'failure', 130 | message: err.toString() 131 | }; 132 | 133 | res.setHeader('content-type', 'text/json'); 134 | res.send(msg); 135 | }); 136 | 137 | 138 | }); 139 | 140 | let getElectionsHandler = async (req, res) => { 141 | try { 142 | let result = await contract.evaluateTransaction('getElections'); 143 | // result = JSON.parse(result); 144 | res.setHeader('content-type', 'text/json'); 145 | res.send(result.toString()); 146 | } catch (e) { 147 | res.send(e); 148 | } 149 | }; 150 | app.get('/getElections', getElectionsHandler); 151 | app.post('/getElections', getElectionsHandler); 152 | 153 | let addCandidateHandler = async (req, res) => { 154 | let name = req.body.name.toString(), 155 | sign = req.body.sign.toString(), 156 | imgAddress = req.body.imgAddress.toString(), 157 | electionId = req.body.electionId.toString(); 158 | 159 | let debug = false; 160 | if (debug) { 161 | res.setHeader('content-type', 'text/json'); 162 | res.send(JSON.stringify({ name, sign, imgAddress, electionId })); 163 | return; 164 | } else { 165 | contract.submitTransaction('addCandidate', name, sign, imgAddress, electionId).then((data) => { 166 | console.log(data); 167 | let msg = { 168 | status: 'success', 169 | message: 'Candidate Added' 170 | }; 171 | res.setHeader('content-type', 'text/json'); 172 | res.send(msg); 173 | }).catch((err) => { 174 | console.log(err); 175 | let msg = { 176 | status: 'failure', 177 | message: err.toString() 178 | }; 179 | res.setHeader('content-type', 'text/json'); 180 | res.send(msg); 181 | }); 182 | } 183 | }; 184 | app.post('/addCandidate', addCandidateHandler); 185 | 186 | let getCandidatesHandler = async (req, res) => { 187 | let electionId = req.body.electionId.toString(); 188 | 189 | let debug = false; 190 | if (debug) { 191 | res.setHeader('content-type', 'text/json'); 192 | res.send(electionId); 193 | return; 194 | } else { 195 | contract.evaluateTransaction('getCandidates', electionId).then((data) => { 196 | console.log(data.toString()); 197 | res.setHeader('content-type', 'text/json'); 198 | res.send(data.toString()); 199 | }).catch((err) => { 200 | console.log(err); 201 | let msg = { 202 | status: 'failure', 203 | message: err.toString() 204 | }; 205 | res.setHeader('content-type', 'text/json'); 206 | res.send(msg); 207 | }); 208 | } 209 | }; 210 | app.post('/getCandidates', getCandidatesHandler); 211 | 212 | 213 | let registerUserHandler = async (req, res) => { 214 | let name = req.body.name.toString(), 215 | email = req.body.email.toString(), 216 | v1 = req.body.v1.toString(), 217 | v2 = req.body.v2.toString(), 218 | v3 = req.body.v3.toString(), 219 | electionId = req.body.electionId.toString(); 220 | 221 | 222 | let debug = false; 223 | if (debug) { 224 | res.setHeader('content-type', 'text/json'); 225 | res.send(JSON.stringify({ name, email, v1, v2, v3, electionId })); 226 | return; 227 | } else { 228 | contract.submitTransaction('registerVoter', name, email, v1, v2, v3, electionId).then((data) => { 229 | console.log(data); 230 | let msg = { 231 | status: 'success', 232 | data: data.toString(), 233 | message: 'Voter Added' 234 | }; 235 | res.setHeader('content-type', 'text/json'); 236 | res.send(msg); 237 | }).catch((err) => { 238 | console.log(err); 239 | let msg = { 240 | status: 'failure', 241 | message: err.toString() 242 | }; 243 | res.setHeader('content-type', 'text/json'); 244 | res.send(msg); 245 | }); 246 | } 247 | }; 248 | app.post('/registerVoter', registerUserHandler); 249 | 250 | let getLoginChallengeHandler = async (req, res) => { 251 | let result = await contract.evaluateTransaction('getLoginChallenge'); 252 | // result = JSON.parse(result); 253 | res.setHeader('content-type', 'text/json'); 254 | res.send(result); 255 | }; 256 | app.get('/getLoginChallenge', getLoginChallengeHandler); 257 | app.post('/getLoginChallenge', getLoginChallengeHandler); 258 | 259 | 260 | let voterLoginHandler = async (req, res) => { 261 | let email = req.body.email.toString(), 262 | x = req.body.x.toString(), 263 | a1 = req.body.a1.toString(), 264 | a2 = req.body.a2.toString(), 265 | a3 = req.body.a3.toString(), 266 | v1 = req.body.v1.toString(), 267 | v2 = req.body.v2.toString(), 268 | v3 = req.body.v3.toString(), 269 | y1 = req.body.y1.toString(), 270 | electionID = req.body.electionID.toString(); 271 | 272 | let debug = false; 273 | if (debug) { 274 | res.setHeader('content-type', 'text/json'); 275 | res.send(JSON.stringify({ email, x, a1, a2, a3, v1, v2, v3, y1, electionID })); 276 | return; 277 | } else { 278 | try { 279 | contract.evaluateTransaction('voterLogin', email, x, a1, a2, a3, v1, v2, v3, y1, electionID).then((data) => { 280 | console.log(data); 281 | let msg = { 282 | status: 'success', 283 | data: JSON.parse(data), 284 | message: '' 285 | }; 286 | res.setHeader('content-type', 'text/json'); 287 | res.send(msg); 288 | }).catch((err) => { 289 | console.log(err); 290 | let msg = { 291 | status: 'failure', 292 | message: err.toString() 293 | }; 294 | res.setHeader('content-type', 'text/json'); 295 | res.send(msg); 296 | }); 297 | } catch (e) { 298 | res.send(e); 299 | } 300 | contract.evaluateTransaction('voterLogin', email, x, a1, a2, a3, v1, v2, v3, y1, electionID).then((data) => { 301 | console.log(data); 302 | let msg = { 303 | status: 'success', 304 | data: JSON.parse(data), 305 | message: '' 306 | }; 307 | res.setHeader('content-type', 'text/json'); 308 | res.send(msg); 309 | }).catch((err) => { 310 | console.log(err); 311 | let msg = { 312 | status: 'failure', 313 | message: err.toString() 314 | }; 315 | res.setHeader('content-type', 'text/json'); 316 | res.send(msg); 317 | }); 318 | } 319 | }; 320 | app.post('/voterLogin', voterLoginHandler); 321 | 322 | let castVoteHandler = async (req, res) => { 323 | let id = req.body.id.toString(), 324 | voteContent = req.body.voteContent.toString(); 325 | 326 | let debug = false; 327 | if (debug) { 328 | res.setHeader('content-type', 'text/json'); 329 | res.send(JSON.stringify({ id, voteContent })); 330 | } else { 331 | try { 332 | let data = await contract.evaluateTransaction('castVote', id, voteContent); 333 | if (data === null) { 334 | throw new Error(400); 335 | } 336 | 337 | contract.submitTransaction('castVote', id, voteContent).then((data) => { 338 | console.log(data); 339 | let msg = { 340 | status: 'success', 341 | data: JSON.parse(data), 342 | message: 'Vote Cast Successful' 343 | }; 344 | res.setHeader('content-type', 'text/json'); 345 | res.send(msg); 346 | }).catch((err) => { 347 | console.log(err); 348 | let msg = { 349 | status: 'failure', 350 | message: err.toString() 351 | }; 352 | res.setHeader('content-type', 'text/json'); 353 | res.send(msg); 354 | }); 355 | 356 | } catch (e) { 357 | console.log(e); 358 | } 359 | } 360 | }; 361 | app.post('/castVote', castVoteHandler); 362 | 363 | 364 | let calculateResultHandler = async (req, res) => { 365 | let electionId = req.body.electionId.toString(); 366 | 367 | let debug = false; 368 | if (debug) { 369 | res.setHeader('content-type', 'text/json'); 370 | res.send(concat(electionId)); 371 | return; 372 | } else { 373 | res.setHeader('content-type', 'text/json'); 374 | 375 | contract.evaluateTransaction('calculateResult', electionId).then((data) => { 376 | let ret = JSON.parse(data.toString()); 377 | 378 | contract.evaluateTransaction('getCandidates', electionId).then((data) => { 379 | ret.candidates = JSON.parse(data.toString()); 380 | 381 | res.send(JSON.stringify(ret)); 382 | }); 383 | }).catch((err) => { 384 | console.log(err.toString()); 385 | res.setHeader('content-type', 'text/json'); 386 | 387 | let ret = { 388 | id: 'LnfgDsc2WD8F2qNfHK5aResult', 389 | publisherID: err.toString(), 390 | values: [ 391 | 0, 392 | 0, 393 | 0, 394 | 0 395 | ], 396 | electionID: 'LnfgDsc2WD8F2qNfHK5a', 397 | doctype: 'ElectionResult' 398 | }; 399 | 400 | contract.evaluateTransaction('getCandidates', electionId).then((data) => { 401 | ret.candidates = JSON.parse(data.toString()); 402 | 403 | res.send(JSON.stringify(ret)); 404 | }); 405 | }); 406 | } 407 | }; 408 | app.post('/calculateResult', calculateResultHandler); 409 | 410 | 411 | let startElectionHandler = async (req, res) => { 412 | let electionId = req.body.electionId.toString(); 413 | 414 | let debug = false; 415 | if (debug) { 416 | res.setHeader('content-type', 'text/json'); 417 | res.send(concat(electionId)); 418 | return; 419 | } else { 420 | contract.submitTransaction('startElection', electionId).then((data) => { 421 | console.log(data); 422 | let msg = { 423 | status: 'success', 424 | data: data.toString(), 425 | message: '' 426 | }; 427 | res.setHeader('content-type', 'text/json'); 428 | res.send(msg); 429 | }).catch((err) => { 430 | console.log(err); 431 | let msg = { 432 | status: 'failure', 433 | message: err.toString() 434 | }; 435 | res.setHeader('content-type', 'text/json'); 436 | res.send(msg); 437 | }); 438 | } 439 | }; 440 | app.post('/startElection', startElectionHandler); 441 | 442 | 443 | app.get('/', function (req, res) { 444 | res.sendFile(__dirname + '/files/' + 'index.html'); 445 | }); 446 | 447 | 448 | //let's create a post request endpoint at /insertMovie 449 | app.post('/insertMovie', async function (req, res) { 450 | let name = req.body.name; 451 | let director = req.body.director; 452 | let genre = req.body.genre; 453 | let year = req.body.year; 454 | 455 | //now I have to call the chaincode 456 | //we are going to get that code from the invoke.js file 457 | 458 | let key = makeid(20); 459 | 460 | await contract.submitTransaction('createMovie', key, name, director, year, genre); 461 | res.setHeader('content-type', 'text/json'); 462 | res.send(JSON.stringify({ key, name, director, genre, year })); 463 | 464 | console.log('record added'); 465 | }); 466 | 467 | app.get('/viewAllMovies', async function (req, res) { 468 | const result = await contract.evaluateTransaction('queryAllMovies'); 469 | 470 | let resultData = result.toString(); 471 | // let movies = JSON.parse(resultData); 472 | 473 | let html = `${resultData}`; 474 | 475 | 476 | res.setHeader('content-type', 'text/json'); 477 | res.send(html); 478 | }); 479 | 480 | app.post('/queryMoviesByYear', async function (req, res) { 481 | let year = req.body.year; 482 | 483 | //now I have to call the chaincode 484 | //we are going to get that code from the invoke.js file 485 | 486 | const result = await contract.evaluateTransaction('queryMoviesByYear', year); 487 | res.setHeader('content-type', 'text/json'); 488 | res.send(result.toString()); 489 | 490 | console.log('query complete'); 491 | }); 492 | 493 | app.post('/queryMoviesByGenre', async function (req, res) { 494 | let genre = req.body.genre; 495 | 496 | //now I have to call the chaincode 497 | //we are going to get that code from the invoke.js file 498 | 499 | const result = await contract.evaluateTransaction('queryMoviesByGenre', genre); 500 | res.setHeader('content-type', 'text/json'); 501 | res.send(result.toString()); 502 | 503 | console.log('query complete'); 504 | }); 505 | 506 | app.post('/deleteAll', async function (req, res) { 507 | await contract.submitTransaction('deleteAll'); 508 | 509 | res.setHeader('content-type', 'text/json'); 510 | res.send(JSON.stringify({ 511 | status: 'Deletion Completed' 512 | })); 513 | }); 514 | 515 | const server = app.listen(PORT, function () { 516 | let host = server.address().address; 517 | let port = server.address().port; 518 | 519 | console.log('Example app listening at http://%s:%s', host, port); 520 | }); 521 | //////////////////////////////////// 522 | ////////////////////////////////////////////////// 523 | 524 | 525 | } catch (error) { 526 | console.error(`Failed to evaluate transaction: ${error}`); 527 | process.exit(1); 528 | } 529 | } 530 | 531 | main(); 532 | -------------------------------------------------------------------------------- /javascript/enrollAdmin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const FabricCAServices = require('fabric-ca-client'); 8 | const { FileSystemWallet, X509WalletMixin } = require('fabric-network'); 9 | const fs = require('fs'); 10 | const path = require('path'); 11 | 12 | const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json'); 13 | console.log(ccpPath); 14 | const ccpJSON = fs.readFileSync(ccpPath, 'utf8'); 15 | console.log(ccpJSON); 16 | const ccp = JSON.parse(ccpJSON); 17 | 18 | 19 | async function main() { 20 | try { 21 | 22 | // Create a new CA client for interacting with the CA. 23 | const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; 24 | const caTLSCACerts = caInfo.tlsCACerts.pem; 25 | const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); 26 | 27 | // Create a new file system based wallet for managing identities. 28 | const walletPath = path.join(process.cwd(), 'wallet'); 29 | const wallet = new FileSystemWallet(walletPath); 30 | console.log(`Wallet path: ${walletPath}`); 31 | 32 | // Check to see if we've already enrolled the admin user. 33 | const adminExists = await wallet.exists('admin'); 34 | if (adminExists) { 35 | console.log('An identity for the admin user "admin" already exists in the wallet'); 36 | return; 37 | } 38 | 39 | // Enroll the admin user, and import the new identity into the wallet. 40 | const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); 41 | const identity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes()); 42 | await wallet.import('admin', identity); 43 | console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); 44 | 45 | } catch (error) { 46 | console.error(`Failed to enroll admin user "admin": ${error}`); 47 | process.exit(1); 48 | } 49 | } 50 | 51 | main(); 52 | -------------------------------------------------------------------------------- /javascript/files/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | My Site 9 | 10 | 11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
Insert a movie

23 |
24 | 25 |

26 | 27 | 28 |

29 | 30 | 31 |

32 | 33 | 34 |

35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 |
Search movies by year:

46 |
47 | 48 |

49 | 50 | 51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
Search movies by genre:

60 |
61 | 62 |

63 | 64 | 65 |
66 |
67 | 68 |
69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /javascript/invoke.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { FileSystemWallet, Gateway } = require('fabric-network'); 8 | const path = require('path'); 9 | 10 | const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json'); 11 | 12 | async function main() { 13 | try { 14 | 15 | // Create a new file system based wallet for managing identities. 16 | const walletPath = path.join(process.cwd(), 'wallet'); 17 | const wallet = new FileSystemWallet(walletPath); 18 | console.log(`Wallet path: ${walletPath}`); 19 | 20 | // Check to see if we've already enrolled the user. 21 | const userExists = await wallet.exists('user1'); 22 | if (!userExists) { 23 | console.log('An identity for the user "user1" does not exist in the wallet'); 24 | console.log('Run the registerUser.js application before retrying'); 25 | return; 26 | } 27 | 28 | // Create a new gateway for connecting to our peer node. 29 | const gateway = new Gateway(); 30 | await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } }); 31 | 32 | // Get the network (channel) our contract is deployed to. 33 | const network = await gateway.getNetwork('mychannel'); 34 | 35 | // Get the contract from the network. 36 | const contract = network.getContract('fabcar'); 37 | 38 | // Submit the specified transaction. 39 | // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') 40 | // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR10', 'Dave') 41 | await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom'); 42 | console.log('Transaction has been submitted'); 43 | 44 | // Disconnect from the gateway. 45 | await gateway.disconnect(); 46 | 47 | } catch (error) { 48 | console.error(`Failed to submit transaction: ${error}`); 49 | process.exit(1); 50 | } 51 | } 52 | 53 | main(); 54 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fabcar", 3 | "version": "1.0.0", 4 | "description": "FabCar application implemented in JavaScript", 5 | "engines": { 6 | "node": ">=8", 7 | "npm": ">=5" 8 | }, 9 | "scripts": { 10 | "lint": "eslint .", 11 | "pretest": "npm run lint", 12 | "test": "nyc mocha --recursive" 13 | }, 14 | "engineStrict": true, 15 | "author": "Hyperledger", 16 | "license": "Apache-2.0", 17 | "dependencies": { 18 | "express": "^4.17.1", 19 | "fabric-ca-client": "~1.4.0", 20 | "fabric-network": "~1.4.0" 21 | }, 22 | "devDependencies": { 23 | "chai": "^4.2.0", 24 | "eslint": "^5.9.0", 25 | "mocha": "^5.2.0", 26 | "nyc": "^13.1.0", 27 | "sinon": "^7.1.1", 28 | "sinon-chai": "^3.3.0" 29 | }, 30 | "nyc": { 31 | "exclude": [ 32 | "coverage/**", 33 | "test/**" 34 | ], 35 | "reporter": [ 36 | "text-summary", 37 | "html" 38 | ], 39 | "all": true, 40 | "check-coverage": true, 41 | "statements": 100, 42 | "branches": 100, 43 | "functions": 100, 44 | "lines": 100 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /javascript/query.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { FileSystemWallet, Gateway } = require('fabric-network'); 8 | const path = require('path'); 9 | 10 | const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json'); 11 | 12 | async function main() { 13 | try { 14 | 15 | // Create a new file system based wallet for managing identities. 16 | const walletPath = path.join(process.cwd(), 'wallet'); 17 | const wallet = new FileSystemWallet(walletPath); 18 | console.log(`Wallet path: ${walletPath}`); 19 | 20 | // Check to see if we've already enrolled the user. 21 | const userExists = await wallet.exists('user1'); 22 | if (!userExists) { 23 | console.log('An identity for the user "user1" does not exist in the wallet'); 24 | console.log('Run the registerUser.js application before retrying'); 25 | return; 26 | } 27 | 28 | // Create a new gateway for connecting to our peer node. 29 | const gateway = new Gateway(); 30 | await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } }); 31 | 32 | // Get the network (channel) our contract is deployed to. 33 | const network = await gateway.getNetwork('mychannel'); 34 | 35 | // Get the contract from the network. 36 | const contract = network.getContract('fabcar'); 37 | 38 | // Evaluate the specified transaction. 39 | // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') 40 | // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') 41 | const result = await contract.evaluateTransaction('getDateTime', ''); 42 | console.log(`Transaction has been evaluated, result is: ${result.toString()}`); 43 | 44 | } catch (error) { 45 | console.error(`Failed to evaluate transaction: ${error}`); 46 | process.exit(1); 47 | } 48 | } 49 | 50 | main(); 51 | -------------------------------------------------------------------------------- /javascript/registerUser.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { FileSystemWallet, Gateway, X509WalletMixin } = require('fabric-network'); 8 | const path = require('path'); 9 | 10 | const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json'); 11 | 12 | async function main() { 13 | try { 14 | 15 | // Create a new file system based wallet for managing identities. 16 | const walletPath = path.join(process.cwd(), 'wallet'); 17 | const wallet = new FileSystemWallet(walletPath); 18 | console.log(`Wallet path: ${walletPath}`); 19 | 20 | // Check to see if we've already enrolled the user. 21 | const userExists = await wallet.exists('user1'); 22 | if (userExists) { 23 | console.log('An identity for the user "user1" already exists in the wallet'); 24 | return; 25 | } 26 | 27 | // Check to see if we've already enrolled the admin user. 28 | const adminExists = await wallet.exists('admin'); 29 | if (!adminExists) { 30 | console.log('An identity for the admin user "admin" does not exist in the wallet'); 31 | console.log('Run the enrollAdmin.js application before retrying'); 32 | return; 33 | } 34 | 35 | // Create a new gateway for connecting to our peer node. 36 | const gateway = new Gateway(); 37 | await gateway.connect(ccpPath, { wallet, identity: 'admin', discovery: { enabled: true, asLocalhost: true } }); 38 | 39 | // Get the CA client object from the gateway for interacting with the CA. 40 | const ca = gateway.getClient().getCertificateAuthority(); 41 | const adminIdentity = gateway.getCurrentIdentity(); 42 | 43 | // Register the user, enroll the user, and import the new identity into the wallet. 44 | const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'user1', role: 'client' }, adminIdentity); 45 | const enrollment = await ca.enroll({ enrollmentID: 'user1', enrollmentSecret: secret }); 46 | const userIdentity = X509WalletMixin.createIdentity('Org1MSP', enrollment.certificate, enrollment.key.toBytes()); 47 | await wallet.import('user1', userIdentity); 48 | console.log('Successfully registered and enrolled admin user "user1" and imported it into the wallet'); 49 | 50 | } catch (error) { 51 | console.error(`Failed to register user "user1": ${error}`); 52 | process.exit(1); 53 | } 54 | } 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /javascript/run.sh: -------------------------------------------------------------------------------- 1 | cd .. && ./startFabric.sh && cd javascript && rm -rf wallet/ && node enrollAdmin.js && node registerUser.js && node app.js 2 | -------------------------------------------------------------------------------- /kill.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ../first-network/ 4 | 5 | ./byfn.sh down 6 | 7 | cd ../fabcar 8 | 9 | docker rm -f $(docker ps -aq) 10 | 11 | docker rmi -f $(docker images | grep fabcar | awk '{print $3}') 12 | -------------------------------------------------------------------------------- /startFabric.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # Exit on first error 8 | set -e 9 | 10 | # don't rewrite paths for Windows Git Bash users 11 | export MSYS_NO_PATHCONV=1 12 | starttime=$(date +%s) 13 | CC_SRC_LANGUAGE=${1:-"go"} 14 | CC_SRC_LANGUAGE=`echo "$CC_SRC_LANGUAGE" | tr [:upper:] [:lower:]` 15 | if [ "$CC_SRC_LANGUAGE" = "go" -o "$CC_SRC_LANGUAGE" = "golang" ]; then 16 | CC_RUNTIME_LANGUAGE=golang 17 | CC_SRC_PATH=github.com/chaincode/fabcar/go 18 | elif [ "$CC_SRC_LANGUAGE" = "java" ]; then 19 | CC_RUNTIME_LANGUAGE=java 20 | CC_SRC_PATH=/opt/gopath/src/github.com/chaincode/fabcar/java 21 | elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then 22 | CC_RUNTIME_LANGUAGE=node # chaincode runtime language is node.js 23 | CC_SRC_PATH=/opt/gopath/src/github.com/chaincode/fabcar/javascript 24 | elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then 25 | CC_RUNTIME_LANGUAGE=node # chaincode runtime language is node.js 26 | CC_SRC_PATH=/opt/gopath/src/github.com/chaincode/fabcar/typescript 27 | echo Compiling TypeScript code into JavaScript ... 28 | pushd ../chaincode/fabcar/typescript 29 | npm install 30 | npm run build 31 | popd 32 | echo Finished compiling TypeScript code into JavaScript 33 | else 34 | echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script 35 | echo Supported chaincode languages are: go, javascript, and typescript 36 | exit 1 37 | fi 38 | 39 | 40 | # clean the keystore 41 | rm -rf ./hfc-key-store 42 | 43 | # launch network; create channel and join peer to channel 44 | cd ../first-network 45 | echo y | ./byfn.sh down 46 | echo y | ./byfn.sh up -a -n -s couchdb 47 | 48 | CONFIG_ROOT=/opt/gopath/src/github.com/hyperledger/fabric/peer 49 | ORG1_MSPCONFIGPATH=${CONFIG_ROOT}/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 50 | ORG1_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 51 | ORG2_MSPCONFIGPATH=${CONFIG_ROOT}/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 52 | ORG2_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 53 | ORDERER_TLS_ROOTCERT_FILE=${CONFIG_ROOT}/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 54 | set -x 55 | 56 | echo "Installing smart contract on peer0.org1.example.com" 57 | docker exec \ 58 | -e CORE_PEER_LOCALMSPID=Org1MSP \ 59 | -e CORE_PEER_ADDRESS=peer0.org1.example.com:7051 \ 60 | -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \ 61 | -e CORE_PEER_TLS_ROOTCERT_FILE=${ORG1_TLS_ROOTCERT_FILE} \ 62 | cli \ 63 | peer chaincode install \ 64 | -n fabcar \ 65 | -v 1.0 \ 66 | -p "$CC_SRC_PATH" \ 67 | -l "$CC_RUNTIME_LANGUAGE" 68 | 69 | echo "Installing smart contract on peer0.org2.example.com" 70 | docker exec \ 71 | -e CORE_PEER_LOCALMSPID=Org2MSP \ 72 | -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 \ 73 | -e CORE_PEER_MSPCONFIGPATH=${ORG2_MSPCONFIGPATH} \ 74 | -e CORE_PEER_TLS_ROOTCERT_FILE=${ORG2_TLS_ROOTCERT_FILE} \ 75 | cli \ 76 | peer chaincode install \ 77 | -n fabcar \ 78 | -v 1.0 \ 79 | -p "$CC_SRC_PATH" \ 80 | -l "$CC_RUNTIME_LANGUAGE" 81 | 82 | echo "Instantiating smart contract on mychannel" 83 | docker exec \ 84 | -e CORE_PEER_LOCALMSPID=Org1MSP \ 85 | -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \ 86 | cli \ 87 | peer chaincode instantiate \ 88 | -o orderer.example.com:7050 \ 89 | -C mychannel \ 90 | -n fabcar \ 91 | -l "$CC_RUNTIME_LANGUAGE" \ 92 | -v 1.0 \ 93 | -c '{"Args":[]}' \ 94 | -P "AND('Org1MSP.member','Org2MSP.member')" \ 95 | --tls \ 96 | --cafile ${ORDERER_TLS_ROOTCERT_FILE} \ 97 | --peerAddresses peer0.org1.example.com:7051 \ 98 | --tlsRootCertFiles ${ORG1_TLS_ROOTCERT_FILE} 99 | 100 | echo "Waiting for instantiation request to be committed ..." 101 | sleep 10 102 | 103 | echo "Submitting initLedger transaction to smart contract on mychannel" 104 | echo "The transaction is sent to the two peers with the chaincode installed (peer0.org1.example.com and peer0.org2.example.com) so that chaincode is built before receiving the following requests" 105 | docker exec \ 106 | -e CORE_PEER_LOCALMSPID=Org1MSP \ 107 | -e CORE_PEER_MSPCONFIGPATH=${ORG1_MSPCONFIGPATH} \ 108 | cli \ 109 | peer chaincode invoke \ 110 | -o orderer.example.com:7050 \ 111 | -C mychannel \ 112 | -n fabcar \ 113 | -c '{"function":"initLedger","Args":[]}' \ 114 | --waitForEvent \ 115 | --tls \ 116 | --cafile ${ORDERER_TLS_ROOTCERT_FILE} \ 117 | --peerAddresses peer0.org1.example.com:7051 \ 118 | --peerAddresses peer0.org2.example.com:9051 \ 119 | --tlsRootCertFiles ${ORG1_TLS_ROOTCERT_FILE} \ 120 | --tlsRootCertFiles ${ORG2_TLS_ROOTCERT_FILE} 121 | 122 | set +x 123 | 124 | cat <