Customising hyperledger explorer to search through content in hyperledger blockchain

Hyperledger is an open source blockchain solution that you can use for setting up private blockchains. The product is becoming more useful in providing blockchain solutions to industries but there is still little documentation on how to make it as customisable at possible.

The instructions for setting up hyperledger can be found here fabric installation and setup . Instructions for setting up blockchain explorer are also available here blockexplorer for hyperledger.

We will focus now searching though the blockchain with details saved in a specific transaction in hyperledger. The blockchain explorer makes use of a postgres database. By default the blochain explorer can search through the blockchain using transaction hash, block hash and channel names, it is however not configured to search through information specific to a transaction. This information is saved in the column write_set of the table transactions in the postgres database and the datatype of the column is json.

The blockchain explorer has APIs which you can also extend to make your own APIs that you can use to search through the blockchain.

  • Creating your own customised query to search through transactions and get a list.
    You will need to update the file blockexplorer/blockchain-explorer/app/persistence/postgreSQL/CRUDService.js

In this file you will find the following piece of code, this will be your guide to creating a new function with your custom query.

getTxList(channel_genesis_hash, blockNum, txid, from, to, orgs) {

let  orgsSql  =  '';

if (orgs  &&  orgs  !=  '') {

orgsSql  =  `and t.creator_msp_id in (${orgs})`;

}
  let  sqlTxList  =  ` select t.creator_msp_id,t.txhash,t.type,t.chaincodename,t.createdt,channel.name as channelName from transactions as t

inner join channel on t.channel_genesis_hash=channel.channel_genesis_hash where t.blockid >= ${blockNum} and t.id >= ${txid}  ${orgsSql} and

t.channel_genesis_hash = '${channel_genesis_hash}' and t.createdt between '${from}' and '${to}' order by t.id desc`;

return  sql.getRowsBySQlQuery(sqlTxList);

}

From this query above it will search through the transactions and provide you a list of results. We can then create our own function that can search through the data to also find those matching the information that was part of the transaction. Below is an example of how such a function can look like.

getTxListWithOtherInfo(channel_genesis_hash, blockNum, txid, from, to, orgs,otherInfoToSearch) {
let  orgsSql  =  '';
if (orgs  &&  orgs  !=  '') {
orgsSql  =  `and t.creator_msp_id in (${orgs})`;
}
let  sqlTxList  =  ` select t.creator_msp_id,t.txhash,t.type,t.chaincodename,t.createdt,channel.name as channelName from transactions as t
inner join channel on t.channel_genesis_hash=channel.channel_genesis_hash where t.blockid >= ${blockNum} and t.id >= ${txid}  ${orgsSql} and
t.channel_genesis_hash = '${channel_genesis_hash}' and t.createdt between '${from}' and '${to}' and t.write_set::json->>0 LIKE '%${otherInfoToSearch}%' order by t.id desc`;
    return  sql.getRowsBySQlQuery(sqlTxList);
}

Take note of this part of code here

 and t.write_set::json->>0 LIKE '%${otherInfoToSearch}%'

This is the part the searches through the json datatype column and converting the data to a string using ->> and looking for a match with the resultant string text.

Common problem
A common problem encountered is that of the search failing because of invalid values caused by \u0000 which is imported as part of the data by the explorer which its building the database and filling it with data from the blockchain. A solution for this is to remove this as data is being imported so that it is not saved in the database. This can be archived by updating the file
blockexplorer/blockchain-explorer/app/persistence/postgreSQL/db/pgservice.js

In this file We update the function saveRow and update it to this.

 function  saveRow(tablename, columnValues) {

return  new  Promise(function(resolve, reject) {
var  addSqlParams  = [];
var  updatesqlcolumn  = [];
var  updatesqlflag  = [];
var  i  =  1; 
Object.keys(columnValues).forEach(k  => {
 let  v  =  columnValues[k];  
console.log(v);  
if(tablename==="transactions"){

if(v  && (k==="read_set"  ||  k==="write_set")){
v  =  v.replace(/\\u0000/gi, "");
}
}
addSqlParams.push(v);
updatesqlcolumn.push(JSON.stringify(k));
updatesqlflag.push('$'  +  i);
i  =  i  +  1; 
});

var  updatesqlparmstr  =  updatesqlcolumn.join(',').replace(/\\u0000/gi, "");
var  updatesqlflagstr  =  updatesqlflag.join(',');
 var  addSql  =  `INSERT INTO ${tablename} ( ${updatesqlparmstr} ) VALUES( ${updatesqlflagstr} ) RETURNING *;`;
logger.debug(`Insert sql is ${addSql}`);
client.query(addSql, addSqlParams, (err, res) => {
if (err) {
logger.error('[INSERT ERROR] - ', err.message);
console.log(err.stack);
reject(err);
}
resolve(res.rows[0].id);  
});  
});   
}

The main focus for this change is on this section:

  if(tablename==="transactions"){
    if(v  && (k==="read_set"  ||  k==="write_set")){
    v  =  v.replace(/\\u0000/gi, "");
    }
    }

With these changes you should be able to do a lot more like creating custom APIs to search through you hyperledger blockchain.

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now