fabric-network: How to listen to events

Listening to events with Fabric Network

This tutorial describes the different ways to listen to events emitted by a network using the fabric-network module.

Overview

There are three event types that can be subscribed to:

  1. Contract events - Those emitted explicitly by the chaincode developer within a transaction
  2. Transaction (Commit) events - Those emitted automatically when a transaction is committed after an invoke
  3. Block events - Those emitted automatically when a block is committed

Listening for these events allows the application to react without directly calling a transaction. This is ideal in use cases such as monitoring network analytics.

Usage

Each listener type takes at least one parameter, the event callback. This is the function that is called when an event is received.

The callback function given is expected to be a promise, meaning that the callback can perform asynchronous tasks without risking missing events.

Options

module:fabric-network.Network~EventListenerOptions.

Note: Listeners will connect to event hubs and ask to receive unfiltered events by default. To receive filtered events, set EventListenerOptions.filtered: true.

Naming

All event listeners (including CommitEventListeners, which use the transaction ID) must have a unique name at the Network level

Contract events

const gateway = new Gateway();
await gateway.connect(connectionProfile, gatewayOptions);
const network = await gateway.getNetwork('mychannel');
const contract = network.getContract('my-contract');

/**
 * @param {String} listenerName the name of the event listener
 * @param {String} eventName the name of the event being listened to
 * @param {Function} callback the callback function with signature (error, event, blockNumber, transactionId, status)
 * @param {module:fabric-network.Network~EventListenerOptions} options
**/
const listener = await contract.addContractListener('my-contract-listener', 'sale', (err, event, blockNumber, transactionId, status) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(`Block Number: ${blockNumber} Transaction ID: ${transactionId} Status: ${status}`);
})

Notice that there is no need to specify an event hub, as the EventHubSelectionStrategy will select it automatically.

Block events

const gateway = new Gateway();
await gateway.connect(connectionProfile, gatewayOptions);
const network = await gateway.getNetwork('mychannel');

/**
 * @param {String} listenerName the name of the event listener
 * @param {Function} callback the callback function with signature (error, blockNumber, transactionId, status)
 * @param {module:fabric-network.Network~EventListenerOptions} options
**/
const listener = await network.addBlockListener('my-block-listener', (error, block) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(`Block: ${block}`);
});

When listening for block events, it is important to specify if you want a filtered or none filtered event, as this determines which event hub is compatible with the request.

Commit events

Note: The listener listener name is transactionId.<some random string>

There are two methods for subscribing to a transaction commit event. Using module:fabric-network.Network and directly, using module:fabric-network.Transaction. Using module:fabric-network.Transaction directly, abstracts away the need to specify which transaction ID you wish to listen for.

Option 1

const gateway = new Gateway();
await gateway.connect(connectionProfile, gatewayOptions);
const network = await gateway.getNetwork('mychannel');
const contract = network.getContract('my-contract');

const transaction = contract.newTransaction('sell');
/**
 * @param {String} transactionId the transaction ID
 * @param {Function} callback the callback function with signature (error, transactionId, status, blockNumber)
 * @param {Object} options
**/
const listener = await network.addCommitListener(transaction.getTransactionID().getTransactionID(), (err, transactionId, status, blockNumber) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(`Transaction ID: ${transactionId} Status: ${status} Block number: ${blockNumber}`);
});

Option 2

const gateway = new Gateway();
await gateway.connect(connectionProfile, gatewayOptions);
const network = await gateway.getNetwork('mychannel');
const contract = network.getContract('my-contract');

const transaction = contract.newTransaction('sell');
/**
 * @param {String} transactionId the transaction ID
 * @param {Function} callback the callback function with signature (error, transactionId, status, blockNumber)
 * @param {Object} options
**/
const listener = await transaction.addCommitListener((err, transactionId, status, blockNumber) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(`Transaction ID: ${transactionId} Status: ${status} Block number: ${blockNumber}`);
});

Both Network.addCommitListener and Contract.addCommitListener have an optional eventHub parameter. When set, the listener will only listen to that event hub, and in the event of an unforeseen disconnect, it will try and to reconnect without using the EventHubSelectionStrategy.

Checkpointing

fabric-network: How to replay missed events

Start Block and End Block

In the module:fabric-network~EventListenerOptions it is possible to specify a startBlock and an endBlock. This behaves in the same way as the same options on ChannelEventHub shown in the tutorial here fabric-client: How to use the channel-based event service. Using startBlock and endBlock disables event replay using a checkpointer for the events received by that listener.

Unregistering listeners

addContractListener, addBlockListener and addCommitListener return a ContractEventListener, BlockEventListener and CommitEventListener respectively. Each has an unregister() function that removes the listener from the event hub, meaning no further events will be received from that listener until register() is called again.