UNPKG

fabric-network

Version:

SDK for writing node.js applications to interact with Hyperledger Fabric. This package encapsulates the APIs to connect to a Fabric network, submit transactions and perform queries against the ledger.

324 lines • 14.2 kB
"use strict"; /* * Copyright 2018, 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.NetworkImpl = void 0; const contract_1 = require("./contract"); const blockeventsource_1 = require("./impl/event/blockeventsource"); const commitlistenersession_1 = require("./impl/event/commitlistenersession"); const eventservicemanager_1 = require("./impl/event/eventservicemanager"); const isolatedblocklistenersession_1 = require("./impl/event/isolatedblocklistenersession"); const listeners_1 = require("./impl/event/listeners"); const listenersession_1 = require("./impl/event/listenersession"); const sharedblocklistenersession_1 = require("./impl/event/sharedblocklistenersession"); const gatewayutils_1 = require("./impl/gatewayutils"); const Logger = require("./logger"); const logger = Logger.getLogger('Network'); async function listenerOptionsWithDefaults(options) { var _a; const defaultOptions = { type: 'full' }; const result = Object.assign(defaultOptions, options); const checkpointBlock = await ((_a = options.checkpointer) === null || _a === void 0 ? void 0 : _a.getBlockNumber()); if (checkpointBlock) { result.startBlock = checkpointBlock; } return result; } /** * <p>A Network represents the set of peers in a Fabric network. * Applications should get a Network instance using the * gateway's [getNetwork]{@link module:fabric-network.Gateway#getNetwork} method.</p> * * <p>The Network object provides the ability for applications to:</p> * <ul> * <li>Obtain a specific smart contract deployed to the network using [getContract]{@link module:fabric-network.Network#getContract}, * in order to submit and evaluate transactions for that smart contract.</li> * <li>Listen to new block events and replay previous block events using * [addBlockListener]{@link module:fabric-network.Network#addBlockListener}.</li> * </ul> * @interface Network * @memberof module:fabric-network */ /** * Get the owning Gateway connection. * @method Network#getGateway * @memberof module:fabric-network * @returns {module:fabric-network.Gateway} A Gateway. */ /** * Get an instance of a contract (chaincode) on the current network. * @method Network#getContract * @memberof module:fabric-network * @param {string} chaincodeId - the chaincode identifier. * @param {string} [name] - the name of the contract. * @param {string[]} [collections] - the names of collections defined for this chaincode. * @returns {module:fabric-network.Contract} the contract. */ /** * Get the underlying channel object representation of this network. * @method Network#getChannel * @memberof module:fabric-network * @returns {Channel} A channel. */ /** * Add a listener to receive transaction commit and peer disconnect events for a set of peers. This is typically used * only within the implementation of a custom [transaction commit event handler]{@tutorial transaction-commit-events}. * @method Network#addCommitListener * @memberof module:fabric-network * @param {module:fabric-network.CommitListener} listener A transaction commit listener callback function. * @param {Endorser[]} peers The peers from which to receive events. * @param {string} transactionId A transaction ID. * @returns {module:fabric-network.CommitListener} The added listener. * @example * const listener: CommitListener = (error, event) => { * if (error) { * // Handle peer communication error * } else { * // Handle transaction commit event * } * } * const peers = network.channel.getEndorsers(); * await network.addCommitListener(listener, peers, transactionId); */ /** * Remove a previously added transaction commit listener. * @method Network#removeCommitListener * @memberof module:fabric-network * @param {module:fabric-network.CommitListener} listener A transaction commit listener callback function. */ /** * Add a listener to receive block events for this network. Blocks will be received in order and without duplication. * The default is to listen for full block events from the current block position. * @method Network#addBlockListener * @memberof module:fabric-network * @async * @param {module:fabric-network.BlockListener} listener A block listener callback function. * @param {module:fabric-network.ListenerOptions} [options] Listener options. * @returns {Promise<module:fabric-network.BlockListener>} The added listener. * @example * const listener: BlockListener = async (event) => { * // Handle block event * * // Listener may remove itself if desired * if (event.blockNumber.equals(endBlock)) { * network.removeBlockListener(listener); * } * } * const options: ListenerOptions = { * startBlock: 1 * }; * await network.addBlockListener(listener, options); */ /** * Remove a previously added block listener. * @method Network#removeBlockListener * @memberof module:fabric-network * @param listener {module:fabric-network.BlockListener} A block listener callback function. */ /** * A callback function that will be invoked when a block event is received. * @callback BlockListener * @memberof module:fabric-network * @async * @param {module:fabric-network.BlockEvent} event A block event. * @returns {Promise<void>} */ /** * A callback function that will be invoked when either a peer communication error occurs or a transaction commit event * is received. Only one of the two arguments will have a value for any given invocation. * @callback CommitListener * @memberof module:fabric-network * @param {module:fabric-network.CommitError} [error] Peer communication error. * @param {module:fabric-network.CommitEvent} [event] Transaction commit event from a specific peer. */ /** * @interface CommitError * @extends Error * @memberof module:fabric-network * @property {Endorser} peer The peer that raised this error. */ /** * @interface CommitEvent * @extends {module:fabric-network.TransactionEvent} * @memberof module:fabric-network * @property {Endorser} peer The endorsing peer that produced this event. */ class NetworkImpl { /* * Network constructor for internal use only. * @param {Gateway} gateway The owning gateway instance * @param {Channel} channel The fabric-common channel instance */ constructor(gateway, channel) { this.contracts = new Map(); this.initialized = false; this.commitListeners = new Map(); this.blockListeners = new Map(); const method = 'constructor'; logger.debug('%s - start', method); this.gateway = gateway; this.channel = channel; this.eventServiceManager = new eventservicemanager_1.EventServiceManager(this); this.realtimeFilteredBlockEventSource = new blockeventsource_1.BlockEventSource(this.eventServiceManager, { type: 'filtered' }); this.realtimeFullBlockEventSource = new blockeventsource_1.BlockEventSource(this.eventServiceManager, { type: 'full' }); this.realtimePrivateBlockEventSource = new blockeventsource_1.BlockEventSource(this.eventServiceManager, { type: 'private' }); } getGateway() { return this.gateway; } getContract(chaincodeId, name = '') { const method = 'getContract'; logger.debug('%s - start - name %s', method, name); if (!this.initialized) { throw new Error('Unable to get contract as this network has failed to initialize'); } const key = `${chaincodeId}:${name}`; let contract = this.contracts.get(key); if (!contract) { contract = new contract_1.ContractImpl(this, chaincodeId, name); logger.debug('%s - create new contract %s', method, chaincodeId); this.contracts.set(key, contract); } return contract; } getChannel() { return this.channel; } async addCommitListener(listener, peers, transactionId) { const sessionSupplier = () => Promise.resolve(new commitlistenersession_1.CommitListenerSession(listener, this.eventServiceManager, peers, transactionId)); return await listenersession_1.addListener(listener, this.commitListeners, sessionSupplier); } removeCommitListener(listener) { listenersession_1.removeListener(listener, this.commitListeners); } async addBlockListener(listener, options = {}) { const sessionSupplier = async () => await this.newBlockListenerSession(listener, options); return await listenersession_1.addListener(listener, this.blockListeners, sessionSupplier); } removeBlockListener(listener) { listenersession_1.removeListener(listener, this.blockListeners); } _dispose() { const method = '_dispose'; logger.debug('%s - start', method); this.contracts.clear(); this.commitListeners.forEach((listener) => listener.close()); this.commitListeners.clear(); this.blockListeners.forEach((listener) => listener.close()); this.blockListeners.clear(); this.realtimeFilteredBlockEventSource.close(); this.realtimeFullBlockEventSource.close(); this.realtimePrivateBlockEventSource.close(); this.eventServiceManager.close(); this.channel.close(); this.initialized = false; } /** * Initialize this network instance * @private */ async _initialize(discover) { const method = '_initialize'; logger.debug('%s - start', method); if (this.initialized) { return; } await this._initializeInternalChannel(discover); this.initialized = true; // Must be created after channel initialization to ensure discovery has located the peers const queryOptions = this.gateway.getOptions().queryHandlerOptions; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.queryHandler = queryOptions.strategy(this); logger.debug('%s - end', method); } /** * initialize the channel if it hasn't been done * @private */ async _initializeInternalChannel(options) { const method = '_initializeInternalChannel'; logger.debug('%s - start', method); if (options === null || options === void 0 ? void 0 : options.enabled) { logger.debug('%s - initialize with discovery', method); let targets; logger.debug('%s - user has not specified discovery targets, check channel and client', method); // maybe the channel has connected endorsers with the mspid const mspId = this.gateway.getIdentity().mspId; targets = this.channel.getEndorsers(mspId); if (!targets || targets.length < 1) { // then check the client for connected peers associated with the mspid targets = this.channel.client.getEndorsers(mspId); } if (!targets || targets.length < 1) { // get any peer targets = this.channel.client.getEndorsers(); } if (!targets || targets.length < 1) { throw Error('No discovery targets found'); } else { logger.debug('%s - using channel/client targets', method); } // should have targets by now, create the discoverers from the endorsers const discoverers = []; for (const peer of targets) { const discoverer = this.channel.client.newDiscoverer(peer.name, peer.mspid); discoverer.setEndpoint(peer.endpoint); discoverers.push(discoverer); } this.discoveryService = this.channel.newDiscoveryService(this.channel.name); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const idx = this.gateway.identityContext; // do the three steps this.discoveryService.build(idx); this.discoveryService.sign(idx); logger.debug('%s - will discover asLocalhost:%s', method, options.asLocalhost); await this.discoveryService.send({ asLocalhost: options.asLocalhost, targets: discoverers }); // now we can work with the discovery results // or get a handler later from the discoverService // to be used on endorsement, queries, and commits logger.debug('%s - discovery complete - channel is populated', method); } logger.debug('%s - end', method); } async newBlockListenerSession(listener, options) { options = await listenerOptionsWithDefaults(options); if (options.checkpointer) { listener = listeners_1.checkpointBlockListener(listener, options.checkpointer); } if (gatewayutils_1.notNullish(options.startBlock)) { return this.newIsolatedBlockListenerSession(listener, options); } else { return this.newSharedBlockListenerSession(listener, options.type); } } newIsolatedBlockListenerSession(listener, options) { const blockSource = new blockeventsource_1.BlockEventSource(this.eventServiceManager, options); return new isolatedblocklistenersession_1.IsolatedBlockListenerSession(listener, blockSource); } newSharedBlockListenerSession(listener, type) { if (type === 'filtered') { return new sharedblocklistenersession_1.SharedBlockListenerSession(listener, this.realtimeFilteredBlockEventSource); } else if (type === 'full') { return new sharedblocklistenersession_1.SharedBlockListenerSession(listener, this.realtimeFullBlockEventSource); } else if (type === 'private') { return new sharedblocklistenersession_1.SharedBlockListenerSession(listener, this.realtimePrivateBlockEventSource); } else { throw new Error(`Unsupported event listener type: ${type}`); } } } exports.NetworkImpl = NetworkImpl; //# sourceMappingURL=network.js.map