Skip to content

Commit

Permalink
New apis added for fetch data by blocknumber blockrange transactionid
Browse files Browse the repository at this point in the history
Signed-off-by: Anusha <anu.vp.nair@gmail.com>

Modified function names

Signed-off-by: Anusha <anu.vp.nair@gmail.com>

code formatted as per review comments

Signed-off-by: Anusha <anu.vp.nair@gmail.com>
  • Loading branch information
Anusha-Padmanabhan authored and ArchanaArige committed Feb 6, 2023
1 parent 0db8369 commit f84f298
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 2 deletions.
24 changes: 24 additions & 0 deletions app/persistence/fabric/CRUDService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,30 @@ export class CRUDService {
}
}
// Orderer BE-303

/**
*
* Returns the block by block number.
*
* @param {*} channel_genesis_hash
* @param {*} blockNo
* @returns
* @memberof CRUDService
*/
async getBlockByBlocknum(network_name:any, channel_genesis_hash:any, blockNo:any) {
const sqlBlockTxList = `select a.* from (
select (select c.name from channel c where c.channel_genesis_hash =$1 and c.network_name = $2)
as channelname, blocks.blocknum,blocks.txcount ,blocks.datahash ,blocks.blockhash ,blocks.prehash,blocks.createdt, blocks.blksize, (
SELECT array_agg(txhash) as txhash FROM transactions where blockid = $3 and
channel_genesis_hash = $1 and network_name = $2) from blocks where
blocks.channel_genesis_hash =$1 and blocks.network_name = $2 and blocknum = $3) a where a.txhash IS NOT NULL`;

const row: any = await this.sql.getRowsBySQlCase(
sqlBlockTxList,
[channel_genesis_hash, network_name, blockNo]);
return row;
}

}

/**
Expand Down
192 changes: 192 additions & 0 deletions app/platform/fabric/Proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { NetworkService } from './service/NetworkService';
import { ExplorerError } from '../../common/ExplorerError';
import { explorerError } from '../../common/ExplorerMessage';
import * as FabricConst from './utils/FabricConst';
import { SyncPlatform } from './sync/SyncPlatform';
import { convertValidationCode, jsonObjSize, SyncServices } from './sync/SyncService';
import * as sha from 'js-sha256';
import * as FabricUtils from './utils/FabricUtils';

const fabric_const = FabricConst.fabric.const;

Expand Down Expand Up @@ -405,4 +409,192 @@ export class Proxy {
);
}
}

/**
*
*
* @param {*} channel_genesis_hash
* @param {*} blockNo
* @returns
* @memberof Proxy
*/
async fetchDataByBlockNo(network_id: string, channel_genesis_hash: string, blockNo: number) {
return await this.dataByBlockNo(network_id, channel_genesis_hash, blockNo);
}

/**
*
*
* @param {*} channel_genesis_hash
* @param {*} txnId
* @returns
* @memberof Proxy
*/
async fetchDataByTxnId(network_id: string, channel_genesis_hash: string, txnId: string) {
const results = await this.persistence.getCrudService().getTransactionByID(network_id, channel_genesis_hash, txnId);
if (results == null) {
return await this.queryTxFromLedger(network_id, channel_genesis_hash, txnId);
}
return results;
}

async queryTxFromLedger(network_id: string, channel_genesis_hash: string, txnId: string) {
let syncPlatform = new SyncPlatform(this.persistence, null)
let sync = new SyncServices(syncPlatform, this.persistence);
const client = this.platform.getClient(network_id);
const channel_name = client.getChannelNameByHash(channel_genesis_hash);
try {
const txn = await client.fabricGateway.queryTransaction(channel_name, txnId);
logger.info("Transaction details from query Transaction ", txn);
if (txn) {
//Formatting of transaction details
const txObj = txn.transactionEnvelope;
const txStr = JSON.stringify(txObj);
let txid = txObj.payload.header.channel_header.tx_id;
let validation_code = '';
let payload_proposal_hash = '';
let chaincode = '';
let rwset;
let readSet;
let writeSet;
let chaincodeID;
let mspId = [];

sync.convertFormatOfValue(
'value',
client.fabricGateway.fabricConfig.getRWSetEncoding(),
txObj
);
if (txid && txid !== '') {
const val_code = txn.validationCode;
validation_code = convertValidationCode(val_code);
}
if (txObj.payload.data.actions !== undefined) {
chaincode =
txObj.payload.data.actions[0].payload.action.proposal_response_payload
.extension.chaincode_id.name;
chaincodeID = new Uint8Array(
txObj.payload.data.actions[0].payload.action.proposal_response_payload.extension
);
mspId = txObj.payload.data.actions[0].payload.action.endorsements.map(
endorsement => endorsement.endorser.mspid
);
rwset =
txObj.payload.data.actions[0].payload.action.proposal_response_payload
.extension.results.ns_rwset;

readSet = rwset.map(rw => ({
chaincode: rw.namespace,
set: rw.rwset.reads
}));

writeSet = rwset.map(rw => ({
chaincode: rw.namespace,
set: rw.rwset.writes
}));

payload_proposal_hash = txObj.payload.data.actions[0].payload.action.proposal_response_payload.proposal_hash.toString(
'hex'
);
}
if (txObj.payload.header.channel_header.typeString === 'CONFIG') {
txid = sha.sha256(txStr);
readSet =
txObj.payload.data.last_update.payload?.data.config_update.read_set;
writeSet =
txObj.payload.data.last_update.payload?.data.config_update.write_set;
}

const chaincode_id = String.fromCharCode.apply(null, chaincodeID);
const transaction = {
channel_name,
txhash: txid,
createdt: txObj.payload.header.channel_header.timestamp,
chaincodename: chaincode,
chaincode_id,
creator_msp_id: txObj.payload.header.signature_header.creator.mspid,
endorser_msp_id: mspId,
type: txObj.payload.header.channel_header.typeString,
readSet,
writeSet,
validation_code,
payload_proposal_hash,
};
return transaction;
}
return txn;
}
catch (e) {
logger.debug('No transaction found with this txn id >> ', e);
}
}

/**
*
*
* @param {*} channel_genesis_hash
* @param {*} startBlockNo
* @param {*} endBlockNo
* @returns
* @memberof Proxy
*/
async fetchDataByBlockRange(network_id: string, channel_genesis_hash: string, startBlockNo: number, endBlockNo: number) {
let blockValue, blockArray = [];
for (let index = startBlockNo; index <= endBlockNo; index++) {
blockValue = await this.dataByBlockNo(network_id, channel_genesis_hash, index);
if (blockValue != "response_payloads is null") {
blockArray.push(blockValue);
}
else
break;
}
if (blockArray.length > 0) {
return blockArray;
}
return blockValue;
}

//Re-usable component to fetch data using block no and block range
async dataByBlockNo(network_id: string, channel_genesis_hash: string, blockNo: number) {
const client = this.platform.getClient(network_id);
const channel_name = client.getChannelNameByHash(channel_genesis_hash);
//fetch data from postgress
const results = await this.persistence.getCrudService().getBlockByBlocknum(network_id, channel_genesis_hash, blockNo);
if (results == null) {
const block = await this.getBlockByNumber(network_id, channel_genesis_hash, blockNo);
if (block != "response_payloads is null") {
logger.info("block details from gateway", block);
const first_tx = block.data.data[0];
const header = first_tx.payload.header;
const createdt = await FabricUtils.getBlockTimeStamp(
header.channel_header.timestamp
);
const blockhash = await FabricUtils.generateBlockHash(block.header);
//For transaction id
const txLen = block.data.data.length;
let txArray = [];
for (let txIndex = 0; txIndex < txLen; txIndex++) {
const txObj = block.data.data[txIndex];
let txid = txObj.payload.header.channel_header.tx_id;
txArray.push(txid);
}
const blockData = {
channelname: channel_name,
blocknum: block.header.number.toString(),
datahash: block.header.data_hash.toString('hex'),
prehash: block.header.previous_hash.toString('hex'),
txcount: block.data.data.length,
createdt,
prev_blockhash: '',
blockhash,
channel_genesis_hash,
blksize: jsonObjSize(block),
txhash: txArray
};
return blockData;
}
return block;
}
return results;
}
}
22 changes: 22 additions & 0 deletions app/platform/fabric/gateway/FabricGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,4 +501,26 @@ export class FabricGateway {
}
return orderers;
}

async queryTransaction(channelName:string, txnId:string) {
try {
const network = await this.gateway.getNetwork(this.defaultChannelName);
// Get the contract from the network.
const contract = network.getContract('qscc');
const resultByte = await contract.evaluateTransaction(
'GetTransactionByID',
channelName,
txnId
);
const resultJson = BlockDecoder.decodeTransaction(resultByte);
logger.debug('queryTransaction', resultJson);
return resultJson;
} catch (error) {
logger.error(
`Failed to get transaction ${txnId} from channel ${channelName} : `,
error
);
return null;
}
}
}
4 changes: 2 additions & 2 deletions app/platform/fabric/sync/SyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,15 +758,15 @@ export class SyncServices {
}
}
// Transaction validation code
function convertValidationCode(code) {
export function convertValidationCode(code) {
if (typeof code === 'string') {
return code;
}
return _validation_codes[code];
}

// Calculate data size of json object
function jsonObjSize(json) {
export function jsonObjSize(json) {
let bytes = 0;

function sizeOf(obj) {
Expand Down
67 changes: 67 additions & 0 deletions app/rest/platformroutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,71 @@ export async function platformroutes(
});
});
});

/**
* *
* Block by block number
* GET /fetchDataByBlockNo
* curl -i 'http://<host>:<port>/fetchDataByBlockNo/<channel_genesis_hash>/<blockNo>'
*/
router.get('/fetchDataByBlockNo/:channel_genesis_hash/:blockNo', (req, res) => {
const blockNo = parseInt(req.params.blockNo);
const channel_genesis_hash = req.params.channel_genesis_hash;
proxy.fetchDataByBlockNo(req.network, channel_genesis_hash, blockNo).then((data: any) => {
if (data != "response_payloads is null") {
res.send({ status: 200, data: data });
}
else{
res.send({ status: 404, data: "Block not found" });
}
});
});

/**
* *
* Blocks by block range
* GET /fetchDataByBlockRange
* curl -i 'http://<host>:<port>/fetchDataByBlockRange/<channel_genesis_hash>/<startBlockNo>/<endBlockNo>'
*/
router.get('/fetchDataByBlockRange/:channel_genesis_hash/:startBlockNo/:endBlockNo', (req, res) => {
const startBlockNo = parseInt(req.params.startBlockNo);
const endBlockNo = parseInt(req.params.endBlockNo);
const channel_genesis_hash = req.params.channel_genesis_hash;
if (startBlockNo < endBlockNo) {
proxy.fetchDataByBlockRange(req.network, channel_genesis_hash, startBlockNo, endBlockNo).then((data: any) => {
if (data != "response_payloads is null") {
res.send({ status: 200, data: data });
}
else{
res.send({ status: 404, data: "Block(s) not found" });
}
});
}
else {
return requtil.invalidRequest(req, res);
}

});


/**
* *
* Transaction by txn id
* GET /fetchDataByTxnId
* curl -i 'http://<host>:<port>/fetchDataByTxnId/<channel_genesis_hash>/<txnId>'
*/
router.get('/fetchDataByTxnId/:channel_genesis_hash/:txnId', (req, res) => {
const txnId = req.params.txnId;
const channel_genesis_hash = req.params.channel_genesis_hash;
proxy.fetchDataByTxnId(req.network, channel_genesis_hash, txnId).then((data: any) => {
if (data != null) {
res.send({ status: 200, data: data });
}
else{
res.send({ status: 404, data: "Transaction not found" });
}
});
});


} // End platformroutes()

0 comments on commit f84f298

Please sign in to comment.