Skip to content

Commit

Permalink
Adding Boottime Feature
Browse files Browse the repository at this point in the history
Signed-off-by: Web-Dev-Manoj <manojbhargav.mv@gmail.com>
  • Loading branch information
Web-Dev-Manoj authored and ArchanaArige committed Dec 13, 2023
1 parent fcae98f commit 718e476
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 78 deletions.
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,73 @@ $ npm run build

## Run Hyperledger Explorer

### Bootup Mode

The Bootup Mode feature allows you to specify how many blocks should be loaded when starting the Hyperledger Explorer. You can choose from the below two modes:

- **ALL**: Load all available blocks.
- **CUSTOM**: Load a specific number of blocks as configured in the `config.json` file.

To set the Bootup Mode, update the `app/platform/fabric/config.json` file in your project with the desired mode.

- **ALL**
```json
{
"network-configs": {
"test-network": {
"name": "Test Network",
"profile": "./connection-profile/test-network.json",
"enableAuthentication": false,
"bootMode": "ALL",
"noOfBlocks": 0
}
},
"license": "Apache-2.0"
}
```
**Note:** In ALL Mode, Please make sure that `noOfBlocks` paramater is set to `0`

- **CUSTOM**

The `noOfBlocks` parameter allows you to specify the number of blocks that the Hyperledger Explorer will use when booting up. If you are using custom mode and want to control the number of blocks, make sure to pass your desired value to the `noOfBlocks` parameter.

```json
{
"network-configs": {
"test-network": {
"name": "Test Network",
"profile": "./connection-profile/test-network.json",
"enableAuthentication": false,
"bootMode": "CUSTOM",
"noOfBlocks": 5
}
},
"license": "Apache-2.0"
}
```

**Note:** Setting `noOfBlocks` to `0` will load Hyperledger Explorer with the latest block.

### Bootup Mode example for reference

Let's say your blockchain network consists of a total of 20 blocks, numbered from 1 to 20. You are interested in loading only the latest 5 blocks, which are blocks 20, 19, 18, 17, and 16.
Here is an example of how you can configure Hyperledger Explorer to achieve this:
```json
{
"network-configs": {
"test-network": {
"name": "Test Network",
"profile": "./connection-profile/test-network.json",
"enableAuthentication": false,
"bootMode": "CUSTOM",
"noOfBlocks": 5
}
},
"license": "Apache-2.0"
}
```
### Run Locally in the Same Location
* Modify `app/explorerconfig.json` to update sync settings.
Expand Down
15 changes: 15 additions & 0 deletions app/persistence/fabric/CRUDService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,21 @@ export class CRUDService {
}
// Orderer BE-303

/**
* Returns whether the block is available in the DB or not
*
* @param {*} blockHeight
* @returns
* @memberof CRUDService
*/
async isBlockAvailableInDB(channel_genesis_hash: string, blockHeight: number) {
const count: any = await this.sql.getRowsBySQlCase(
`SELECT COUNT(*) FROM blocks WHERE channel_genesis_hash= $1 AND blocknum = $2`,
[channel_genesis_hash, blockHeight]
);
return count.count > 0;
}

/**
*
* Returns the block by block number.
Expand Down
47 changes: 32 additions & 15 deletions app/platform/fabric/Proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,23 +226,40 @@ export class Proxy {
* @memberof Proxy
*/
async getChannelsInfo(network_id) {
const client = this.platform.getClient(network_id);
const channels = await this.persistence
.getCrudService()
.getChannelsInfo(network_id);
const currentchannels = [];
for (const channel of channels) {
const channel_genesis_hash = client.getChannelGenHash(channel.channelname);
let agoBlockTimes = this.getLatestBlockTime(channel);
if (
channel_genesis_hash &&
channel_genesis_hash === channel.channel_genesis_hash
) {
currentchannels.push({ ...channel, agoBlockTimes });
try {
const client = this.platform.getClient(network_id);
const channels = await this.persistence.getCrudService().getChannelsInfo(network_id);
const updatedChannels = [];

for (const channel of channels) {
const channel_genesis_hash = client.getChannelGenHash(channel.channelname);
let agoBlockTimes = this.getLatestBlockTime(channel);

try {
const chainInfo = await client.fabricGateway.queryChainInfo(channel.channelname);

if (chainInfo && chainInfo.height && chainInfo.height.low >= 0) {
const totalBlocks = chainInfo.height.low;

if (channel_genesis_hash && channel_genesis_hash === channel.channel_genesis_hash) {
updatedChannels.push({ ...channel, totalBlocks, agoBlockTimes });
} else {
updatedChannels.push({ ...channel, totalBlocks });
}
} else {
logger.warn(`Invalid chain information for channel: ${channel.channelname}`);
}
} catch (error) {
logger.error(`Error querying chain information for channel: ${channel.channelname}`, error);
}
}

logger.debug('getChannelsInfo %j', updatedChannels);
return updatedChannels;
} catch (error) {
logger.error("Error querying channel information:", error);
return null;
}
logger.debug('getChannelsInfo >> %j', currentchannels);
return currentchannels;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion app/platform/fabric/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"network-configs": {
"test-network": {
"name": "Test Network",
"profile": "./connection-profile/test-network.json"
"profile": "./connection-profile/test-network.json",
"bootMode": "ALL",
"noOfBlocks": 0
}
},
"license": "Apache-2.0"
Expand Down
143 changes: 112 additions & 31 deletions app/platform/fabric/sync/SyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import fabprotos from 'fabric-protos';
import includes from 'lodash/includes';
import * as sha from 'js-sha256';

import * as PATH from 'path';
import * as fs from 'fs';

import { helper } from '../../../common/helper';

import { ExplorerError } from '../../../common/ExplorerError';
Expand All @@ -16,6 +20,12 @@ const logger = helper.getLogger('SyncServices');

const fabric_const = FabricConst.fabric.const;

const config_path = PATH.resolve(__dirname, '../config.json');
const all_config = JSON.parse(fs.readFileSync(config_path, 'utf8'));
const network_configs = all_config[fabric_const.NETWORK_CONFIGS];

const boot_modes = FabricConst.BootModes;

// Transaction validation code
const _validation_codes = {};
for (const key in fabprotos.protos.TxValidationCode) {
Expand Down Expand Up @@ -356,52 +366,123 @@ export class SyncServices {
.saveChaincodPeerRef(network_id, chaincode_peer_row);
}

/**
*
*
* @param {*} channel_name
* @memberof SyncServices
*/
async syncBlocks(client, channel_name, noDiscovery) {
const network_id = client.getNetworkId();

// Get channel information from ledger
const channelInfo = await client.fabricGateway.queryChainInfo(channel_name);

if (!channelInfo) {
logger.info(`syncBlocks: Failed to retrieve channelInfo >> ${channel_name}`);
return;
}
const synch_key = `${network_id}_${channel_name}`;
logger.info(`syncBlocks: Start >> ${synch_key}`);

// Check if block synchronization is already in process
if (this.synchInProcess.includes(synch_key)) {
logger.info(`syncBlocks: Block sync in process for >> ${synch_key}`);
return;
}
this.synchInProcess.push(synch_key);
try {
// Get channel information from ledger
const channelInfo = await client.fabricGateway.queryChainInfo(channel_name);
if (!channelInfo) {
logger.info(
`syncBlocks: Failed to retrieve channelInfo >> ${channel_name}`
);
return;
}

const channel_genesis_hash = client.getChannelGenHash(channel_name);
const blockHeight = parseInt(channelInfo.height.low) - 1;
// Query missing blocks from DB
const results = await this.persistence
.getMetricService()
.findMissingBlockNumber(network_id, channel_genesis_hash, blockHeight);

if (results) {
for (const result of results) {
// Get block by number
try {
const block = await client.fabricGateway.queryBlock(
channel_name,
result.missing_id
// Getting necessary information from configuration file
const bootMode = network_configs[network_id].bootMode.toUpperCase();
const channel_genesis_hash = client.getChannelGenHash(channel_name);
const latestBlockHeight = parseInt(channelInfo.height.low) - 1;

// Get the value of noOfBlocks from configuration file
let noOfBlocks = 0;

if (bootMode === boot_modes[0]) {
// Sync all available blocks
noOfBlocks = latestBlockHeight + 1;
} else if (bootMode === boot_modes[1]) {
// Get the value of noOfBlocks from configuration file
noOfBlocks = parseInt(network_configs[network_id].noOfBlocks);

if (isNaN(noOfBlocks)) {
logger.error(
'Invalid noOfBlocks configuration, please either provide in numeric eg: (1) or ("1")'
);
if (block) {
return;
}
}

// Calculate the starting block height for sync
const startingBlockHeight = Math.max(0, latestBlockHeight - noOfBlocks + 1);

// Syncing Details
logger.info(
`Syncing blocks from ${startingBlockHeight} to ${latestBlockHeight}`
);

// Load the latest blocks as per the configuration
for (
let blockHeight = latestBlockHeight;
blockHeight >= startingBlockHeight;
blockHeight--
) {
try {
const [block, isBlockAvailable] = await Promise.all([
client.fabricGateway.queryBlock(channel_name, blockHeight),
this.persistence
.getCrudService()
.isBlockAvailableInDB(channel_genesis_hash, blockHeight)
]);

if (block && !isBlockAvailable) {
await this.processBlockEvent(client, block, noDiscovery);
}
logger.info(`Synced block #${blockHeight}`);
} catch {
logger.error(`Failed to process Block # ${result.missing_id}`);
logger.error(`Failed to process Block # ${blockHeight}`);
}
}

const missingBlocks = await this.persistence
.getMetricService()
.findMissingBlockNumber(
network_id,
channel_genesis_hash,
latestBlockHeight
);

if (missingBlocks) {
// Filter missing blocks to start syncing from 'startingBlockHeight'
const missingBlocksToSync = missingBlocks.filter(
missingBlock => missingBlock.missing_id >= startingBlockHeight
);
for (const missingBlock of missingBlocksToSync) {
try {
const block = await client.fabricGateway.queryBlock(
channel_name,
missingBlock.missing_id
);
if (block) {
await this.processBlockEvent(client, block, noDiscovery);
}
logger.info(`Synced missing block #${missingBlock.missing_id}`);
} catch {
logger.error(
`Failed to process Missing Block # ${missingBlock.missing_id}`
);
}
}
} else {
logger.debug('Missing blocks not found for %s', channel_name);
}
} else {
logger.debug('Missing blocks not found for %s', channel_name);
const index = this.synchInProcess.indexOf(synch_key);
this.synchInProcess.splice(index, 1);
logger.info(`syncBlocks: Finish >> ${synch_key}`);
} catch (error) {
logger.error(`Error in syncBlocks: ${error}`);
}
const index = this.synchInProcess.indexOf(synch_key);
this.synchInProcess.splice(index, 1);
logger.info(`syncBlocks: Finish >> ${synch_key}`);
}

async updateDiscoveredChannel(client, channel_name, channel_genesis_hash) {
Expand Down
5 changes: 5 additions & 0 deletions app/platform/fabric/utils/FabricConst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ export const fabric = {
NOTITY_TYPE_CLIENTERROR: '6'
}
};

export enum BootModes {
'ALL',
'CUSTOM'
}
Loading

0 comments on commit 718e476

Please sign in to comment.