github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/build-blockchain-insurance-app-master/web/www/blockchain/utils.js (about) 1 'use strict'; 2 3 import { 4 resolve 5 } from 'path'; 6 import EventEmitter from 'events'; 7 8 import { 9 load as loadProto 10 } from 'grpc'; 11 import Long from 'long'; 12 import hfc from 'fabric-client'; 13 import utils from 'fabric-client/lib/utils'; 14 import Orderer from 'fabric-client/lib/Orderer'; 15 import Peer from 'fabric-client/lib/Peer'; 16 import EventHub from 'fabric-client/lib/EventHub'; 17 import User from 'fabric-client/lib/User'; 18 import CAClient from 'fabric-ca-client'; 19 import { 20 snakeToCamelCase, 21 camelToSnakeCase 22 } from 'json-style-converter'; 23 24 process.env.GOPATH = resolve(__dirname, '../../chaincode'); 25 const JOIN_TIMEOUT = 120000, 26 TRANSACTION_TIMEOUT = 120000; 27 28 export class OrganizationClient extends EventEmitter { 29 30 constructor(channelName, ordererConfig, peerConfig, caConfig, admin) { 31 super(); 32 this._channelName = channelName; 33 this._ordererConfig = ordererConfig; 34 this._peerConfig = peerConfig; 35 this._caConfig = caConfig; 36 this._admin = admin; 37 this._peers = []; 38 this._eventHubs = []; 39 this._client = new hfc(); 40 41 // Setup channel 42 this._channel = this._client.newChannel(channelName); 43 44 // Setup orderer and peers 45 const orderer = this._client.newOrderer(ordererConfig.url, { 46 pem: ordererConfig.pem, 47 'ssl-target-name-override': ordererConfig.hostname 48 }); 49 this._channel.addOrderer(orderer); 50 51 const defaultPeer = this._client.newPeer(peerConfig.url, { 52 pem: peerConfig.pem, 53 'ssl-target-name-override': peerConfig.hostname 54 }); 55 this._peers.push(defaultPeer); 56 this._channel.addPeer(defaultPeer); 57 this._adminUser = null; 58 } 59 60 async login() { 61 try { 62 this._client.setStateStore( 63 await hfc.newDefaultKeyValueStore({ 64 path: `./${this._peerConfig.hostname}` 65 })); 66 this._adminUser = await getSubmitter( 67 this._client, "admin", "adminpw", this._caConfig); 68 } catch (e) { 69 console.log(`Failed to enroll user. Error: ${e.message}`); 70 throw e; 71 } 72 } 73 74 initEventHubs() { 75 // Setup event hubs 76 try { 77 const defaultEventHub = this._client.newEventHub(); 78 defaultEventHub.setPeerAddr(this._peerConfig.eventHubUrl, { 79 pem: this._peerConfig.pem, 80 'ssl-target-name-override': this._peerConfig.hostname 81 }); 82 defaultEventHub.connect(); 83 defaultEventHub.registerBlockEvent( 84 block => { 85 this.emit('block', unmarshalBlock(block)); 86 }); 87 this._eventHubs.push(defaultEventHub); 88 } catch (e) { 89 console.log(`Failed to configure event hubs. Error ${e.message}`); 90 throw e; 91 } 92 } 93 94 async getOrgAdmin() { 95 return this._client.createUser({ 96 username: `Admin@${this._peerConfig.hostname}`, 97 mspid: this._caConfig.mspId, 98 cryptoContent: { 99 privateKeyPEM: this._admin.key, 100 signedCertPEM: this._admin.cert 101 } 102 }); 103 } 104 105 async initialize() { 106 try { 107 await this._channel.initialize(); 108 } catch (e) { 109 console.log(`Failed to initialize chain. Error: ${e.message}`); 110 throw e; 111 } 112 } 113 114 async createChannel(envelope) { 115 const txId = this._client.newTransactionID(); 116 const channelConfig = this._client.extractChannelConfig(envelope); 117 const signature = this._client.signChannelConfig(channelConfig); 118 const request = { 119 name: this._channelName, 120 orderer: this._channel.getOrderers()[0], 121 config: channelConfig, 122 signatures: [signature], 123 txId 124 }; 125 const response = await this._client.createChannel(request); 126 127 // Wait for 5sec to create channel 128 await new Promise(resolve => { 129 setTimeout(resolve, 5000); 130 }); 131 return response; 132 } 133 134 async joinChannel() { 135 try { 136 const genesisBlock = await this._channel.getGenesisBlock({ 137 txId: this._client.newTransactionID() 138 }); 139 const request = { 140 targets: this._peers, 141 txId: this._client.newTransactionID(), 142 block: genesisBlock 143 }; 144 const joinedChannelPromises = this._eventHubs.map(eh => { 145 eh.connect(); 146 return new Promise((resolve, reject) => { 147 let blockRegistration; 148 const cb = block => { 149 clearTimeout(responseTimeout); 150 eh.unregisterBlockEvent(blockRegistration); 151 if (block.data.data.length === 1) { 152 const channelHeader = 153 block.data.data[0].payload.header.channel_header; 154 if (channelHeader.channel_id === this._channelName) { 155 resolve(); 156 } else { 157 reject(new Error('Peer did not join an expected channel.')); 158 } 159 } 160 }; 161 162 blockRegistration = eh.registerBlockEvent(cb); 163 const responseTimeout = setTimeout(() => { 164 eh.unregisterBlockEvent(blockRegistration); 165 reject(new Error('Peer did not respond in a timely fashion!')); 166 }, JOIN_TIMEOUT); 167 }); 168 }); 169 170 const completedPromise = joinedChannelPromises.concat([ 171 this._channel.joinChannel(request) 172 ]); 173 await Promise.all(completedPromise); 174 } catch (e) { 175 console.log(`Error joining peer to channel. Error: ${e.message}`); 176 throw e; 177 } 178 } 179 180 async checkChannelMembership() { 181 try { 182 const { channels } = await this._client.queryChannels(this._peers[0]); 183 if (!Array.isArray(channels)) { 184 return false; 185 } 186 return channels.some(({channel_id}) => channel_id === this._channelName); 187 } catch (e) { 188 return false; 189 } 190 } 191 192 async checkInstalled(chaincodeId, chaincodeVersion, chaincodePath) { 193 let { 194 chaincodes 195 } = await this._channel.queryInstantiatedChaincodes(); 196 if (!Array.isArray(chaincodes)) { 197 return false; 198 } 199 return chaincodes.some(cc => 200 cc.name === chaincodeId && 201 cc.path === chaincodePath && 202 cc.version === chaincodeVersion); 203 } 204 205 async install(chaincodeId, chaincodeVersion, chaincodePath) { 206 const request = { 207 targets: this._peers, 208 chaincodePath, 209 chaincodeId, 210 chaincodeVersion 211 }; 212 213 // Make install proposal to all peers 214 let results; 215 try { 216 results = await this._client.installChaincode(request); 217 } catch (e) { 218 console.log( 219 `Error sending install proposal to peer! Error: ${e.message}`); 220 throw e; 221 } 222 const proposalResponses = results[0]; 223 const allGood = proposalResponses 224 .every(pr => pr.response && pr.response.status == 200); 225 return allGood; 226 } 227 228 async instantiate(chaincodeId, chaincodeVersion, ...args) { 229 let proposalResponses, proposal; 230 const txId = this._client.newTransactionID(); 231 try { 232 const request = { 233 chaincodeType: 'golang', 234 chaincodeId, 235 chaincodeVersion, 236 fcn: 'init', 237 args: marshalArgs(args), 238 txId 239 }; 240 const results = await this._channel.sendInstantiateProposal(request); 241 proposalResponses = results[0]; 242 proposal = results[1]; 243 244 let allGood = proposalResponses 245 .every(pr => pr.response && pr.response.status == 200); 246 247 if (!allGood) { 248 throw new Error( 249 `Proposal rejected by some (all) of the peers: ${proposalResponses}`); 250 } 251 } catch (e) { 252 throw e; 253 } 254 255 try { 256 const request = { 257 proposalResponses, 258 proposal 259 }; 260 const deployId = txId.getTransactionID(); 261 const transactionCompletePromises = this._eventHubs.map(eh => { 262 eh.connect(); 263 264 return new Promise((resolve, reject) => { 265 // Set timeout for the transaction response from the current peer 266 const responseTimeout = setTimeout(() => { 267 eh.unregisterTxEvent(deployId); 268 reject(new Error('Peer did not respond in a timely fashion!')); 269 }, TRANSACTION_TIMEOUT); 270 271 eh.registerTxEvent(deployId, (tx, code) => { 272 clearTimeout(responseTimeout); 273 eh.unregisterTxEvent(deployId); 274 if (code != 'VALID') { 275 reject(new Error( 276 `Peer has rejected transaction with code: ${code}`)); 277 } else { 278 resolve(); 279 } 280 }); 281 }); 282 }); 283 284 transactionCompletePromises.push(this._channel.sendTransaction(request)); 285 await transactionCompletePromises; 286 } catch (e) { 287 throw e; 288 } 289 } 290 291 async invoke(chaincodeId, chaincodeVersion, fcn, ...args) { 292 let proposalResponses, proposal; 293 const txId = this._client.newTransactionID(); 294 try { 295 const request = { 296 chaincodeId, 297 chaincodeVersion, 298 fcn, 299 args: marshalArgs(args), 300 txId 301 }; 302 const results = await this._channel.sendTransactionProposal(request); 303 proposalResponses = results[0]; 304 proposal = results[1]; 305 306 const allGood = proposalResponses 307 .every(pr => pr.response && pr.response.status == 200); 308 309 if (!allGood) { 310 throw new Error( 311 `Proposal rejected by some (all) of the peers: ${proposalResponses}`); 312 } 313 } catch (e) { 314 throw e; 315 } 316 317 try { 318 const request = { 319 proposalResponses, 320 proposal 321 }; 322 323 const transactionId = txId.getTransactionID(); 324 const transactionCompletePromises = this._eventHubs.map(eh => { 325 eh.connect(); 326 327 return new Promise((resolve, reject) => { 328 // Set timeout for the transaction response from the current peer 329 const responseTimeout = setTimeout(() => { 330 eh.unregisterTxEvent(transactionId); 331 reject(new Error('Peer did not respond in a timely fashion!')); 332 }, TRANSACTION_TIMEOUT); 333 334 eh.registerTxEvent(transactionId, (tx, code) => { 335 clearTimeout(responseTimeout); 336 eh.unregisterTxEvent(transactionId); 337 if (code != 'VALID') { 338 reject(new Error( 339 `Peer has rejected transaction with code: ${code}`)); 340 } else { 341 resolve(); 342 } 343 }); 344 }); 345 }); 346 347 transactionCompletePromises.push(this._channel.sendTransaction(request)); 348 try { 349 await transactionCompletePromises; 350 const payload = proposalResponses[0].response.payload; 351 return unmarshalResult([payload]); 352 } catch (e) { 353 throw e; 354 } 355 } catch (e) { 356 throw e; 357 } 358 } 359 360 async query(chaincodeId, chaincodeVersion, fcn, ...args) { 361 const request = { 362 chaincodeId, 363 chaincodeVersion, 364 fcn, 365 args: marshalArgs(args), 366 txId: this._client.newTransactionID(), 367 }; 368 return unmarshalResult(await this._channel.queryByChaincode(request)); 369 } 370 371 async getBlocks(noOfLastBlocks) { 372 if (typeof noOfLastBlocks !== 'number' && 373 typeof noOfLastBlocks !== 'string') { 374 return []; 375 } 376 377 const { 378 height 379 } = await this._channel.queryInfo(); 380 let blockCount; 381 if (height.comp(noOfLastBlocks) > 0) { 382 blockCount = noOfLastBlocks; 383 } else { 384 blockCount = height; 385 } 386 if (typeof blockCount === 'number') { 387 blockCount = Long.fromNumber(blockCount, height.unsigned); 388 } else if (typeof blockCount === 'string') { 389 blockCount = Long.fromString(blockCount, height.unsigned); 390 } 391 blockCount = blockCount.toNumber(); 392 const queryBlock = this._channel.queryBlock.bind(this._channel); 393 const blockPromises = {}; 394 blockPromises[Symbol.iterator] = function* () { 395 for (let i = 1; i <= blockCount; i++) { 396 yield queryBlock(height.sub(i).toNumber()); 397 } 398 }; 399 const blocks = await Promise.all([...blockPromises]); 400 return blocks.map(unmarshalBlock); 401 } 402 } 403 404 /** 405 * Enrolls a user with the respective CA. 406 * 407 * @export 408 * @param {string} client 409 * @param {string} enrollmentID 410 * @param {string} enrollmentSecret 411 * @param {object} { url, mspId } 412 * @returns the User object 413 */ 414 async function getSubmitter( 415 client, enrollmentID, enrollmentSecret, { 416 url, 417 mspId 418 }) { 419 420 try { 421 let user = await client.getUserContext(enrollmentID, true); 422 if (user && user.isEnrolled()) { 423 return user; 424 } 425 426 // Need to enroll with CA server 427 const ca = new CAClient(url, { 428 verify: false 429 }); 430 try { 431 const enrollment = await ca.enroll({ 432 enrollmentID, 433 enrollmentSecret 434 }); 435 user = new User(enrollmentID, client); 436 await user.setEnrollment(enrollment.key, enrollment.certificate, mspId); 437 await client.setUserContext(user); 438 return user; 439 } catch (e) { 440 throw new Error( 441 `Failed to enroll and persist User. Error: ${e.message}`); 442 } 443 } catch (e) { 444 throw new Error(`Could not get UserContext! Error: ${e.message}`); 445 } 446 } 447 448 export function wrapError(message, innerError) { 449 let error = new Error(message); 450 error.inner = innerError; 451 console.log(error.message); 452 throw error; 453 } 454 455 function marshalArgs(args) { 456 if (!args) { 457 return args; 458 } 459 460 if (typeof args === 'string') { 461 return [args]; 462 } 463 464 let snakeArgs = camelToSnakeCase(args); 465 466 if (Array.isArray(args)) { 467 return snakeArgs.map( 468 arg => typeof arg === 'object' ? JSON.stringify(arg) : arg.toString()); 469 } 470 471 if (typeof args === 'object') { 472 return [JSON.stringify(snakeArgs)]; 473 } 474 } 475 476 function unmarshalResult(result) { 477 if (!Array.isArray(result)) { 478 return result; 479 } 480 let buff = Buffer.concat(result); 481 if (!Buffer.isBuffer(buff)) { 482 return result; 483 } 484 let json = buff.toString('utf8'); 485 if (!json) { 486 return null; 487 } 488 let obj = JSON.parse(json); 489 return snakeToCamelCase(obj); 490 } 491 492 function unmarshalBlock(block) { 493 const transactions = Array.isArray(block.data.data) ? 494 block.data.data.map(({ 495 payload: { 496 header, 497 data 498 } 499 }) => { 500 const { 501 channel_header 502 } = header; 503 const { 504 type, 505 timestamp, 506 epoch 507 } = channel_header; 508 return { 509 type, 510 timestamp 511 }; 512 }) : []; 513 return { 514 id: block.header.number.toString(), 515 fingerprint: block.header.data_hash.slice(0, 20), 516 transactions 517 }; 518 }