Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BE 334 fetch data by block number, block range and transaction id - Backend #338

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) => {
Anusha-Padmanabhan marked this conversation as resolved.
Show resolved Hide resolved
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()