Skip to content

Commit

Permalink
feature: validate chainid with a contract (#71)
Browse files Browse the repository at this point in the history
* feature: validate chainid with a contract
  • Loading branch information
mncdg authored Jan 22, 2024
1 parent 67a7b70 commit 5b8f0c7
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 43 deletions.
4 changes: 2 additions & 2 deletions debridge_node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion debridge_node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "debridge_node",
"version": "2.6.1",
"version": "2.6.2",
"description": "",
"author": "debridge-finance",
"private": true,
Expand Down
4 changes: 2 additions & 2 deletions debridge_node/src/modules/api/services/RescanService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
import { ChainConfigService } from '../../chain/config/services/ChainConfigService';
import { ClassicChainConfig } from '../../chain/config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../chain/config/models/configs/EvmChainConfig';
import { AddNewEventsAction } from '../../chain/scanning/services/AddNewEventsAction';

/**
Expand All @@ -18,7 +18,7 @@ export class RescanService {
* @param toBlock
*/
rescan(chainId: number, fromBlock: number, toBlock: number) {
const chainDetail = this.chainConfigService.get(chainId) as ClassicChainConfig;
const chainDetail = this.chainConfigService.get(chainId) as EvmChainConfig;

if (toBlock - fromBlock >= chainDetail.maxBlockRange) {
const e = new HttpException('Out of range', HttpStatus.METHOD_NOT_ALLOWED);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChainConfig } from './ChainConfig';
import { ChainProvider } from '../ChainProvider';

export class ClassicChainConfig extends ChainConfig {
export class EvmChainConfig extends ChainConfig {
debridgeAddr: string;
providers: ChainProvider;
blockConfirmation: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ChainConfig } from '../models/configs/ChainConfig';
import { ChainProvider } from '../models/ChainProvider';
import { ChainProviderDetail } from '../models/ChainProviderDetail';
import { AuthType } from '../enums/AuthType';
import { ClassicChainConfig } from '../models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../models/configs/EvmChainConfig';
import { SolanaChainConfig } from '../models/configs/SolanaChainConfig';

export const solanaChainId = 7565164;
Expand Down Expand Up @@ -40,7 +40,7 @@ export class ChainConfigService {
blockConfirmation: config.blockConfirmation,
maxBlockRange: config.maxBlockRange,
isSolana,
} as ClassicChainConfig);
} as EvmChainConfig);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ChainConfigService } from '../ChainConfigService';
import { ChainProvider } from '../../models/ChainProvider';
import { AddNewEventsAction } from '../../../scanning/services/AddNewEventsAction';
import { ChainScanningService } from '../../../scanning/services/ChainScanningService';
import { ClassicChainConfig } from '../../models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../models/configs/EvmChainConfig';

jest.mock('../../../../../config/chains_config.json', () => {
return [
Expand Down Expand Up @@ -78,7 +78,7 @@ describe('ChainConfigService', () => {
],
}).compile();
service = module.get(ChainConfigService);
chainProvider = (service.get(970) as ClassicChainConfig).providers;
chainProvider = (service.get(970) as EvmChainConfig).providers;
});

describe('ChainConfigService', () => {
Expand All @@ -93,7 +93,7 @@ describe('ChainConfigService', () => {

it('getChainAuth', async () => {
expect(chainProvider.getChainAuth('https://debridge.io')).toBeUndefined();
const authChainProvider = (service.get(972) as ClassicChainConfig).providers;
const authChainProvider = (service.get(972) as EvmChainConfig).providers;
expect(authChainProvider.getChainAuth('debridge.io')).toEqual([{ name: 'Authorization', value: 'Basic YW50b246MTIz' }]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { abi as deBridgeGateAbi } from '../../../../assets/DeBridgeGate.json';
import { Web3Service } from '../../../web3/services/Web3Service';
import { SolanaReaderService } from './SolanaReaderService';
import { ChainConfigService } from '../../config/services/ChainConfigService';
import { ClassicChainConfig } from '../../config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../config/models/configs/EvmChainConfig';
import { InjectRepository } from '@nestjs/typeorm';
import { SupportedChainEntity } from '../../../../entities/SupportedChainEntity';
import { Repository } from 'typeorm';
Expand Down Expand Up @@ -61,9 +61,9 @@ export class AddNewEventsAction {
chainId,
},
});
const chainDetail = this.chainConfigService.get(chainId) as ClassicChainConfig;
const chainDetail = this.chainConfigService.get(chainId) as EvmChainConfig;

const web3 = await this.web3Service.web3HttpProvider(chainDetail.providers);
const web3 = await this.web3Service.web3HttpProvider(chainDetail);

const registerInstance = new web3.eth.Contract(deBridgeGateAbi as any, chainDetail.debridgeAddr);
// @ts-ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DebrdigeApiService } from '../../../external/debridge_api/services/Debr
import { NonceValidationEnum } from '../enums/NonceValidationEnum';
import { ProcessNewTransferResult } from '../entities/ProcessNewTransferResult';
import { ChainConfig } from '../../config/models/configs/ChainConfig';
import { ClassicChainConfig } from '../../config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../config/models/configs/EvmChainConfig';
import { ChainScanningService } from './ChainScanningService';

@Injectable()
Expand Down Expand Up @@ -50,7 +50,7 @@ export class NonceControllingService implements OnModuleInit {
`incorrect nonce error (missed_nonce): nonce: ${transferResult.nonce}; submissionId: ${transferResult.submissionId} chainId: ${chainId}`,
);
if (!chain.isSolana) {
(chain as ClassicChainConfig).providers.setProviderStatus(web3.chainProvider, false);
(chain as EvmChainConfig).providers.setProviderStatus(web3.chainProvider, false);
}
return NonceValidationEnum.MISSED_NONCE;
} else if (transferResult.nonceValidationStatus === NonceValidationEnum.DUPLICATED_NONCE) {
Expand Down
22 changes: 11 additions & 11 deletions debridge_node/src/modules/jobs/services/StartScanningService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Repository } from 'typeorm';
import { Web3Service } from '../../web3/services/Web3Service';
import { ChainScanningService } from '../../chain/scanning/services/ChainScanningService';
import { ChainConfigService } from '../../chain/config/services/ChainConfigService';
import { ClassicChainConfig } from '../../chain/config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../chain/config/models/configs/EvmChainConfig';
import { SolanaChainConfig } from '../../chain/config/models/configs/SolanaChainConfig';

@Injectable()
Expand Down Expand Up @@ -46,21 +46,21 @@ export class StartScanningService implements OnModuleInit {
}
continue;
}
const chainConfigClassic = chainConfig as ClassicChainConfig;
if (chainConfigClassic.maxBlockRange <= 100) {
this.logger.error(`Cant up application maxBlockRange(${chainConfigClassic.maxBlockRange}) < 100`);
const chainConfigEvm = chainConfig as EvmChainConfig;
if (chainConfigEvm.maxBlockRange <= 100) {
this.logger.error(`Cant up application maxBlockRange(${chainConfigEvm.maxBlockRange}) < 100`);
process.exit(1);
}
if (chainConfigClassic.blockConfirmation <= 8) {
this.logger.error(`Cant up application maxBlockRange(${chainConfigClassic.blockConfirmation}) < 8`);
if (chainConfigEvm.blockConfirmation <= 8) {
this.logger.error(`Cant up application maxBlockRange(${chainConfigEvm.blockConfirmation}) < 8`);
process.exit(1);
}

if (!configInDd) {
await this.supportedChainRepository.save({
chainId: chainId,
latestBlock: chainConfigClassic.firstStartBlock,
network: chainConfigClassic.name,
latestBlock: chainConfigEvm.firstStartBlock,
network: chainConfigEvm.name,
});
}
}
Expand All @@ -79,10 +79,10 @@ export class StartScanningService implements OnModuleInit {
if (chainDetail.isSolana) {
continue;
}
const chainConfigClassic = chainDetail as ClassicChainConfig;
const chainConfigEvm = chainDetail as EvmChainConfig;
await Promise.all(
chainConfigClassic.providers.getAllProviders().map(provider => {
return this.web3Service.validateChainId(chainConfigClassic.providers, provider);
chainConfigEvm.providers.getAllProviders().map(provider => {
return this.web3Service.validateChainId(chainConfigEvm, provider);
}),
);
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Account } from 'web3-core';
import { getTokenName } from '../../../../utils/getTokenName';
import { Web3Service } from '../../../web3/services/Web3Service';
import { ChainConfigService } from '../../../chain/config/services/ChainConfigService';
import { ClassicChainConfig } from '../../../chain/config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../../chain/config/models/configs/EvmChainConfig';
import { BundlrStatusEnum } from '../../../../enums/BundlrStatusEnum';
import { SolanaEventsReaderService } from '../../../solana-events-reader/services/SolanaEventsReaderService';
import { PublicKey } from '@solana/web3.js';
Expand Down Expand Up @@ -63,7 +63,7 @@ export class CheckAssetsEventAction extends IAction {
if (!confirmNewAction) {
try {
this.logger.log(`Process debridgeId: ${submission.debridgeId}`);
const chainFromConfig = this.chainConfigService.get(submission.chainFrom) as ClassicChainConfig;
const chainFromConfig = this.chainConfigService.get(submission.chainFrom) as EvmChainConfig;
let tokenName;
let tokenSymbol;
let tokenDecimals;
Expand All @@ -90,7 +90,7 @@ export class CheckAssetsEventAction extends IAction {
}
// if chainFrom is EVM
} else {
const web3 = await this.web3Service.web3HttpProvider(chainFromConfig.providers);
const web3 = await this.web3Service.web3HttpProvider(chainFromConfig);
const deBridgeGateInstance = new web3.eth.Contract(deBridgeGateAbi as any, chainFromConfig.debridgeAddr);
// struct DebridgeInfo {
// uint256 chainId; // native chain id
Expand Down Expand Up @@ -199,8 +199,8 @@ export class CheckAssetsEventAction extends IAction {
}

private async getTokenInfo(nativeChainId: number, nativeTokenAddress: string) {
const tokenChainDetail = this.chainConfigService.get(nativeChainId) as ClassicChainConfig;
const tokenWeb3 = await this.web3Service.web3HttpProvider(tokenChainDetail.providers);
const tokenChainDetail = this.chainConfigService.get(nativeChainId) as EvmChainConfig;
const tokenWeb3 = await this.web3Service.web3HttpProvider(tokenChainDetail);
const nativeTokenInstance = new tokenWeb3.eth.Contract(ERC20Abi as any, nativeTokenAddress);
const tokenName = await getTokenName(nativeTokenInstance, nativeTokenAddress, { logger: this.logger });
const tokenSymbol = await nativeTokenInstance.methods.symbol().call();
Expand Down
8 changes: 4 additions & 4 deletions debridge_node/src/modules/web3/services/Web3Service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Web3Service } from './Web3Service';
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule } from '@nestjs/config';
import { ChainConfigService } from '../../chain/config/services/ChainConfigService';
import { ClassicChainConfig } from '../../chain/config/models/configs/ClassicChainConfig';
import { EvmChainConfig } from '../../chain/config/models/configs/EvmChainConfig';

jest.mock('../../../config/chains_config.json', () => {
return [
Expand Down Expand Up @@ -89,11 +89,11 @@ describe('Web3Service', () => {
});

it('validateChainId', async () => {
const config = chainConfigService.get(970) as ClassicChainConfig;
const config = chainConfigService.get(970) as EvmChainConfig;
jest.spyOn(config.providers, 'setProviderValidationStatus');
await web3Service.validateChainId(config.providers, 'https://debridge.io');
expect(config.providers.setProviderValidationStatus).toHaveBeenCalledWith('https://debridge.io', true);
const notValidConfig = chainConfigService.get(971) as ClassicChainConfig;
const notValidConfig = chainConfigService.get(971) as EvmChainConfig;
const mockExit = jest.spyOn(process, 'exit').mockImplementation(code => {
throw new Error(code.toString());
});
Expand All @@ -102,7 +102,7 @@ describe('Web3Service', () => {
});

it('web3HttpProvider', async () => {
const config = chainConfigService.get(971) as ClassicChainConfig;
const config = chainConfigService.get(971) as EvmChainConfig;
config.providers.setProviderValidationStatus('https://debridge.io', true);
config.providers.setProviderValidationStatus('debridge.io', true);
expect((await web3Service.web3HttpProvider(config.providers)).chainProvider).toBe('debridge.io');
Expand Down
21 changes: 14 additions & 7 deletions debridge_node/src/modules/web3/services/Web3Service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Injectable, Logger } from '@nestjs/common';
import Web3 from 'web3';
import { ConfigService } from '@nestjs/config';
import { ChainProvider } from '../../chain/config/models/ChainProvider';
import { abi as deBridgeGateAbi } from '../../../assets/DeBridgeGate.json';
import { EvmChainConfig } from '../../chain/config/models/configs/EvmChainConfig';

export class Web3Custom extends Web3 {
constructor(readonly chainProvider: string, httpProvider) {
Expand All @@ -23,7 +24,8 @@ export class Web3Service {
return new Web3();
}

async web3HttpProvider(chainProvider: ChainProvider): Promise<Web3Custom> {
async web3HttpProvider(chainConfig: EvmChainConfig): Promise<Web3Custom> {
const chainProvider = chainConfig.providers;
for (const provider of [...chainProvider.getNotFailedProviders(), ...chainProvider.getFailedProviders()]) {
if (this.providersMap.has(provider)) {
const web3 = this.providersMap.get(provider);
Expand All @@ -49,7 +51,7 @@ export class Web3Service {
continue;
}
if (!chainProvider.getProviderValidationStatus(provider)) {
await this.validateChainId(chainProvider, provider);
await this.validateChainId(chainConfig, provider);
}
chainProvider.setProviderStatus(provider, true);
this.providersMap.set(provider, web3);
Expand All @@ -74,17 +76,22 @@ export class Web3Service {
return false;
}

async validateChainId(chainProvider: ChainProvider, provider: string) {
async validateChainId(chainConfig: EvmChainConfig, provider: string) {
const chainProvider = chainConfig.providers;
try {
const httpProvider = new Web3Custom.providers.HttpProvider(provider, {
timeout: this.web3Timeout,
keepAlive: false,
headers: chainProvider.getChainAuth(provider),
});
const web3 = new Web3Custom(provider, httpProvider);
const web3ChainId = await web3.eth.getChainId();
if (web3ChainId !== chainProvider.getChainId()) {
this.logger.error(`Checking correct RPC from config is failed (in config ${chainProvider.getChainId()} in rpc ${web3ChainId})`);
const contractInstance = new web3.eth.Contract(deBridgeGateAbi as any, chainConfig.debridgeAddr);
// @ts-ignore
web3.eth.setProvider = contractInstance.setProvider;

const contractChainId = Number(await contractInstance.methods.getChainId().call());
if (contractChainId !== chainProvider.getChainId()) {
this.logger.error(`Checking correct RPC from config is failed (in config ${chainProvider.getChainId()} in contract ${contractChainId})`);
process.exit(1);
}
chainProvider.setProviderValidationStatus(provider, true);
Expand Down

0 comments on commit 5b8f0c7

Please sign in to comment.