How Hyperledger Fabric Node.js uses channel-based event services

Keywords: Java network PHP Javascript

This tutorial illustrates the use of channel-based events. These events are similar to existing events, but specific to a single channel. When setting up listeners, clients have some new options for handling channel-based events. Starting with v1.1, channel-based events are a new feature of the Hyperledger Fabric Node.js client.

For more information about Fabric's introduction, see Build your first network perhaps Hand-in-hand teaching you to walk into Hyperledger Fabric.

The following assumptions are about understanding Fabric networks (orderers and peers) and Node application development, including using Javascript Promise.

Summary

Client applications can use the Fabric Node.js client to register "listeners" to receive blocks when adding blocks to the channel ledger. We call these "channel-based events" that allow clients to start receiving blocks from specific block numbers, allowing event processing to work properly on potentially lost blocks. Fabric Node.js clients can also assist client applications by processing incoming blocks and looking for specific transaction or chain code events. This allows client applications to be notified of transaction completion or arbitrary Chain Code events without having to perform multiple queries or search blocks upon receipt.

The service allows any user to receive "filtered" block events (in other words, without sensitive information). Receiving "unfiltered" block events requires read access to channels. The default behavior is to connect to receive filtered block events. To connect to receive unfiltered block events, call connect (true) (see below).

Note that if you register block events and submit transactions, no assumptions should be made about blocks containing transactions. In particular, you should not assume that your transaction is in the block associated with the first block event received after registering with the peer's channel-based event service. Instead, you can register only one transaction.

API on Channel

  • New Channel EventHub (peer): Channel instance method for obtaining new instances of Channel EventHub.
  • GetChannelEventHubs ForOrg: Get an organization-based list of ChannelEventHubs. If the organization name is omitted, the current organization of the current user is used.

New Channel EventHub and API in v1.1:

  • RegiserBlock Event (EvetCallBack, errorCallBack, options): Register block events.
  • unregisterBlockEvent(reg_num): Delete block registration.
  • registerTxEvent(tx_id, eventCallBack, errorCallBack, options): Register specific transaction events.
  • Unregister TxEvent (tx_id): Delete a specific transaction registration.
  • Register Chaincode Event (ccid, eventCallBack, errorCallBack, options): Register chain code events.
  • Unregister Chaincode Event (cc_handle): Delete chain code event registration.
  • connect(full_block): Connect the client channel event center to the event service based on the structure channel. This call must be made before your ChannelEventHub instance receives the event. When a channel-based event center connects to a service, it requests a receiving block or a filtered block. If the full_block parameter is omitted, the default is false and the block that requests filtering is made. After calling connect(), the receiving block or filtered block cannot be changed.
  • disconnect(): Close the connection between client channel event routing and event service based on structured network channel, and notify all current channel event registrations of closure using registered errorCallBacks.

Node parameter

This parameter must be included when obtaining a new instance of ChannelEventHub. When using a connection profile, this value can be the name of a Peer instance or node, see How to Use Public Network Profile.

EvetCallback parameter

This parameter must be included. This is a callback function that is notified when this channel receives a new block while listening for a particular transaction or chain code event.

ErorCallback parameter

This is an optional parameter. This is the callback function to be notified when the channel event routing is closed. Closure may be caused by structural network errors, network connection problems, or by calling disconnect() methods.

options parameter

This is an optional parameter. This parameter will contain the following optional attributes:

  • {integer} startBlock (optional): This option is set to the start block number of the event check. When included, the peer is required to start sending the block number from the channel-based event service. This is also how to restore the missing blocks added to the ledger by listening or replay ing. The default value is the number of the last block in the ledger. Reply events can confuse other event listeners; therefore, when using startBlock and endBlock, only one listener is allowed on ChannelEventHub. When this parameter is excluded (because it will work), the event service is required to start sending blocks from the last block on the ledger.
  • {integer} endBlock (optional): This option is set to the end block number of the event check. When included, the peer's channel-based event service is required to stop sending the block after delivering the block. This is how to replay the missing blocks added to the ledger. If the startBlock is not included, the endBlock must be equal to or greater than the current channel block height. Reply events can confuse other event listeners; therefore, when using startBlock and endBlock, only one listener is allowed on ChannelEventHub.
  • {boolean} unregister (optional): This option setting indicates that registration should be deleted (unregistered) when an event is seen. When an application uses a timeout to wait only for a specified time to view a transaction, the timeout processing should include a manual "fetch-cancel registration" by the transaction event listener to avoid accidental callbacks. For different types of event listeners, the default values for this setting are different. For block listeners, when end_block is set as an option, the default value is true. For the transaction listener, the default value is true. For chain code listeners, the default value is false, because matching filters may be applicable to many transactions.
  • {boolean} disconnect (optional): This option setting indicates that the ChannelEventHub instance automatically disconnects itself from its peer's channel-based event service after seeing the event. Unless endBlock is set, the default value is false, and it will be true.

Getting Channel-based Event Routing

A new method has been added to the Channel object of Fabric Node.js client to simplify the setting of ChannelEventHub object. Use the following command to get the ChannelEventHub instance that will be set to use with the peer's channel-based event service. The ChannelEventHub instance will use all the same endpoint configuration settings that the peer instance is using, such as tls certificates and host and port addresses.

Use the Connection Profile (see Reference material ) The name of the node can be used to obtain the new channel event routing.

var channel_event_hub = channel.newChannelEventHub('peer0.org1.example.com');

The following is an example of how to get the channel event routing list when using the connection profile. The following will obtain a list based on the current organization defined in the current active client section of the connection profile. Objects defined in the organization that set EvetSource to true will be added to the list.

var channel_event_hubs = channel.getChannelEventHubsForOrg();

When creating a node instance, you can use the node instance to get the ChannelEventHub instance.

let data = fs.readFileSync(path.join(__dirname, 'somepath/tlscacerts/org1.example.com-cert.pem'));
let peer = client.newPeer(
    'grpcs://localhost:7051',
    {
        pem: Buffer.from(data).toString(),
        'ssl-target-name-override': 'peer0.org1.example.com'
    }
);
let channel_event_hub = channel.newChannelEventHub(peer);

Block listener

When you need to monitor new blocks to be added to the ledger, use a block event listener. When a new block is submitted to the ledger on the node, the Fabric client Node.js is notified. Then, the client Node.js calls the registered callback of the application. The callback passes the JSON representation of the newly added block. Note that when connect() is not called using the true value, the callback receives the filter block. The access rights of users registered to receive the complete block will be checked by the channel-based event service of the node. When you need to view previously added blocks, the registration of callbacks can include the start block number. The callback will begin to receive blocks from this number and continue to receive new blocks when added to the ledger. This is a way for applications resume and replace to lose events when the application is offline. The application should remember the last block it has processed to avoid replay ing the entire ledger.

The following example registers block listeners to start receiving blocks.

// keep the block_reg to unregister with later if needed
block_reg = channel_event_hub.registerBlockEvent((block) => {
    console.log('Successfully received the block event');
    <do something with the block>
}, (error)=> {
    console.log('Failed to receive the block event ::'+error);
    <do something with the error>
});

The following example uses the start block number to register because the application needs to recover and replace lost blocks in a particular block. Application callbacks will process replay blocks in the same area as current events. Block listeners will continue to receive blocks because they have been submitted to the node's ledger.

// keep the block_reg to unregister with later if needed
block_reg = channel_event_hub.registerBlockEvent((block) => {
    console.log('Successfully received the block event');
    <do something with the block>
}, (error)=> {
    console.log('Failed to receive the block event ::'+error);
    <do something with the error>
},
    {startBlock:23}
);

The following example uses the start block number and the end block for registration. The application needs to replace lost blocks. The application callback handles the replay block in the same area as the current event. When the listener sees the end block event, the block listener automatically cancels registration, and ChannelEventHub closes. The application will not have to process this handle.

block_reg = channel_event_hub.registerBlockEvent((block) => {
    console.log('Successfully received the block event');
    <do something with the block>
}, (error)=> {
    console.log('Failed to receive the block event ::'+error);
    <do something with the error>
},
    // for block listeners, the defaults for unregister and disconnect are true,
    // so the they are not required to be set in the following example
    {startBlock:23, endBlock:30, unregister: true, disconnect: true}
);

Transaction Monitor

Use a transaction listener when you need to monitor the completion of an organization's peer transaction. When a new block is submitted to the ledger on the node, the client is notified of Node.js. Then, the client checks whether the block has registered a transaction identifier. If a transaction is found, the callback will be notified by the transaction ID, transaction status and block number. The filtered block contains the transaction status, so the complete block can be received without connecting to the peer's channel-based event service. Since most non-administrator users will not be able to see the complete block, when these users only need to listen for their submitted transactions, the block connected to the receiving filter will avoid access problems.

The following example will show registering the transaction ID in the javascript commitment and building another commitment to send the transaction to the subscriber. The two commitments will be implemented together so that the results of the two actions can be received together. With a transaction listener, the default option to cancel registration is set to true. Therefore, in the following example, the registered listener will automatically cancel registration after the listener sees the transaction.

let tx_object = client.newTransactionID();

// get the transaction ID string for later use
let tx_id = tx_object.getTransactionID();

let request = {
    targets : targets,
    chaincodeId: 'my_chaincode',
    fcn: 'invoke',
    args: ['doSomething', 'with this data'],
    txId: tx_object
};

return channel.sendTransactionProposal(request);
}).then((results) => {
// a real application would check the proposal results
console.log('Successfully endorsed proposal to invoke chaincode');

// start block may be null if there is no need to resume or replay
let start_block = getBlockFromSomewhere();

let event_monitor = new Promise((resolve, reject) => {
    let handle = setTimeout(() => {
        // do the housekeeping when there is a problem
        channel_event_hub.unregisterTxEvent(tx_id);
        console.log('Timeout - Failed to receive the transaction event');
        reject(new Error('Timed out waiting for block event'));
    }, 20000);

    channel_event_hub.registerTxEvent((event_tx_id, status, block_num) => {
        clearTimeout(handle);
        //channel_event_hub.unregisterTxEvent(event_tx_id); let the default do this
        console.log('Successfully received the transaction event');
        storeBlockNumForLater(block_num);
        resolve(status);
    }, (error)=> {
        clearTimeout(handle);
        console.log('Failed to receive the transaction event ::'+error);
        reject(error);
    },
        // when this `startBlock` is null (the normal case) transaction
        // checking will start with the latest block
        {startBlock:start_block}
        // notice that `unregister` is not specified, so it will default to true
        // `disconnect` is also not specified and will default to false
    );
});
let send_trans = channel.sendTransaction({proposalResponses: results[0], proposal: results[1]});

return Promise.all([event_monitor, send_trans]);
}).then((results) => {

Chaincode event listener

When you need to monitor events that will be published in your chain code, use the chain code event listener. When a new block is submitted to the ledger, the client is notified of Node.js. Then, the client checks the registered chain code pattern in the name field of the chain code event. The registration of listeners includes regular expressions for checking chain code event names. If the chain code event name matches the regular expression of the listener, the callback of the listener will be notified by the chain code event, block number, transaction ID and transaction status. The filtered block will not have chain code event payload information; it only has the chaincode event name. If payload information is required, the user must be able to access the complete block, and the channel event center must connect (true) to receive the complete block event from the peer's channel-based event service.

The following example demonstrates how to register chain code event listeners in javascript commitments and build another commitment to send transactions to subscribers. The two commitments will be implemented together so that the results of the two actions can be received together. If chaincode event listeners are required for long-term monitoring, follow the block listener example above.

let tx_object = client.newTransactionID();
let request = {
    targets : targets,
    chaincodeId: 'my_chaincode',
    fcn: 'invoke',
    args: ['doSomething', 'with this data'],
    txId: tx_object
};

return channel.sendTransactionProposal(request);
}).then((results) => {
// a real application would check the proposal results
console.log('Successfully endorsed proposal to invoke chaincode');

// Build the promise to register a event listener with the NodeSDK.
// The NodeSDK will then send a request to the peer's channel-based event
// service to start sending blocks. The blocks will be inspected to see if
// there is a match with a chaincode event listener.
let event_monitor = new Promise((resolve, reject) => {
    let regid = null;
    let handle = setTimeout(() => {
        if (regid) {
            // might need to do the clean up this listener
            channel_event_hub.unregisterChaincodeEvent(regid);
            console.log('Timeout - Failed to receive the chaincode event');
        }
        reject(new Error('Timed out waiting for chaincode event'));
    }, 20000);

    regid = channel_event_hub.registerChaincodeEvent(chaincode_id.toString(), '^evtsender*',
        (event, block_num, txnid, status) => {
        // This callback will be called when there is a chaincode event name
        // within a block that will match on the second parameter in the registration
        // from the chaincode with the ID of the first parameter.
        console.log('Successfully got a chaincode event with transid:'+ txnid + ' with status:'+status);

        // might be good to store the block number to be able to resume if offline
        storeBlockNumForLater(block_num);

        // to see the event payload, the channel_event_hub must be connected(true)
        let event_payload = event.payload.toString('utf8');
        if(event_payload.indexOf('CHAINCODE') > -1) {
            clearTimeout(handle);
            // Chaincode event listeners are meant to run continuously
            // Therefore the default to automatically unregister is false
            // So in this case we want to shutdown the event listener once
            // we see the event with the correct payload
            channel_event_hub.unregisterChaincodeEvent(regid);
            console.log('Successfully received the chaincode event on block number '+ block_num);
            resolve('RECEIVED');
        } else {
            console.log('Successfully got chaincode event ... just not the one we are looking for on block number '+ block_num);
        }
    }, (error)=> {
        clearTimeout(handle);
        console.log('Failed to receive the chaincode event ::'+error);
        reject(error);
    }
        // no options specified
        // startBlock will default to latest
        // endBlock will default to MAX
        // unregister will default to false
        // disconnect will default to false
    );
});

// build the promise to send the proposals to the orderer
let send_trans = channel.sendTransaction({proposalResponses: results[0], proposal: results[1]});

// now that we have two promises all set to go... execute them
return Promise.all([event_monitor, send_trans]);
}).then((results) => {

======================================================================

Share some interactive online programming practical tutorials related to block chains such as Bitcoin, ETF, EOS, Fabric, etc.

  • java Bitcoin Development Tutorial This course is for beginners. It covers the core concepts of Bitcoin, such as block chain storage, decentralized consensus mechanism, key and script, transaction and UTXO. It also explains in detail how to integrate Bitcoin support functions in Java code, such as creating addresses, managing wallets, building bare transactions, etc. It is a rare Bitcoin development course for Java engineers. .
  • php Bitcoin Development Tutorial This course is for beginners. It covers the core concepts of Bitcoin, such as block chain storage, decentralized consensus mechanism, key and script, transaction and UTXO. It also explains in detail how to integrate Bitcoin support functions in Php code, such as creating addresses, managing wallets, building bare transactions, etc. It is a rare Bitcoin development course for Php engineers.
  • c # Bitcoin Development Tutorial This course is for beginners. It covers the core concepts of Bitcoin, such as block chain storage, decentralized consensus mechanism, key and script, transaction and UTXO. It also explains in detail how to integrate Bitcoin support functions in C# code, such as creating addresses, managing wallets, building bare transactions, etc. It is a rare Bitcoin development course for C# engineers.
  • java Ethernet workshop development tutorial It is mainly for java and android programmers to develop block chain etheric workshop web3j in detail.
  • python etheric workshop Mainly for python engineers to use web3.py for block chain Ethernet development in detail.
  • php Ethernet It mainly introduces the use of php for intelligent contract development interaction, account creation, transactions, transfer, token development, filters and transactions.
  • Introductory Course of ETF This paper mainly introduces the development of intelligent contract and dapp application, which is suitable for introduction.
  • Advanced Course of ETF Development It mainly introduces how to use node.js, mongodb, block chain and ipfs to realize the de-centralized e-commerce DApp, which is suitable for advanced stage.
  • ERC721 Etaifangtong Confirmation Battle The course takes the creation and sharing of digital works of art as the main line of DApp's practical development, and deeply explains the concept, standards and development plan of the heterogeneous passport in Yifang. The content includes the autonomous implementation of ERC-721 standard, explaining the second development of OpenZeppelin contract code base, and using Truffle and IPFS to implement the licensing and decentralization of the licensing exchange.
  • C# Ethernet It mainly explains how to use C# to develop Ethernet Application Based on. Net, including account management, status and transaction, intelligent contract development and interaction, filters and transactions.
  • Introduction to EOS This course helps you get a quick start on the development of decentralized application of EOS block chain. It covers the core knowledge points of EOS toolchain, account and wallet, issuing tokens, intelligent contract development and deployment, code and intelligent contract interaction, and finally completes the development of a note DApp with all knowledge points.
  • Deeply and Simply Play EOS Wallet Development This course takes the complete development process of mobile EOS wallet as the main line, and deeply studies the application development of EOS block chain. It covers the core concepts of EOS block chain such as account, computing resources, intelligent contract, action and transaction. It also explains how to access EOS block chain using eosjs and eosjs-ecc development packages, and how to integrate the support of EOS block chain in React front-end applications. . The course content is simple and in-depth, which is very suitable for front-end engineers to study EOS block chain application development in depth.
  • Hyperledger Fabric Block Chain Development This course is for beginners. It includes the core concepts of Hyperledger Fabric's identity certificate and MSP service, privilege policy, channel configuration and start-up, chain code communication interface, etc. It also includes the operation practice of Fabric network design, node JS chain code and application development. It is the best choice for Nodejs engineers to learn Fabric block chain development.
  • Hyperledger Fabric java Block Chain Development The course is for beginners. It includes the core concepts of Hyperledger Fabric's identity certificate and MSP service, privilege policy, channel configuration and start-up, chain code communication interface, and the operation practice of Fabric network design, java chain code and application development. It is the best choice for java engineers to learn Fabric block chain development.
  • Detailed description of tendermint block chain development This course is suitable for engineers who want to use tendermint to develop block chains. It includes the core concepts of tendermint application development model, such as ABCI interface, Merkel tree, multi-version state library, and rich practical codes such as token issuance. It is the best choice for go language engineers to develop fast-start block chains.

Huizhi original translation, reprinted please indicate the source. Here is How Hyperledger Fabric node.js uses channel-based event services

Posted by impulse() on Mon, 22 Apr 2019 13:36:34 -0700