Skip to content
Open
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
5 changes: 5 additions & 0 deletions debridge_node/src/entities/SubmissionEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SubmisionStatusEnum } from '../enums/SubmisionStatusEnum';
import { SubmisionAssetsStatusEnum } from '../enums/SubmisionAssetsStatusEnum';
import { UploadStatusEnum } from '../enums/UploadStatusEnum';
import { BundlrStatusEnum } from '../enums/BundlrStatusEnum';
import { RpcValidationStatusEnum } from '../enums/RpcValidationStatusEnum';

@Entity('submissions')
@Unique(['submissionId'])
Expand Down Expand Up @@ -74,6 +75,10 @@ export class SubmissionEntity {
@Index()
bundlrStatus: BundlrStatusEnum;

@Column({ nullable: true })
@Index()
rpcValidationStatus: RpcValidationStatusEnum;

@Column()
@Index()
assetsStatus: SubmisionAssetsStatusEnum;
Expand Down
4 changes: 4 additions & 0 deletions debridge_node/src/enums/RpcValidationStatusEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum RpcValidationStatusEnum {
NEW = 1,
VALIDATED = 2,
}
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
Expand Up @@ -86,6 +86,15 @@ export class ChainProvider {
return details.isActive;
}

/**
* Get require to provider
* @param {string} provider
*/
getRequireConfirmation(provider: string): boolean {
const details = this.providers.get(provider);
return details.requireConfirmation;
}

/**
* Set validation status to provider
* @param {string} provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface ChainProviderDetail {
user?: string;
password?: string;
authType: AuthType;
requireConfirmation?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export class ChainConfig {
name: string;
isSolana: boolean;
interval: number;
rpcConfirmation: number;
}
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';

/**
Expand All @@ -26,6 +26,7 @@ export class ChainConfigService {
name: config.name,
interval: config.interval,
lastTransaction: config.lastTransaction,
rpcConfirmation: config.rpcConfirmation,
isSolana,
} as SolanaChainConfig);
} else {
Expand All @@ -38,8 +39,9 @@ export class ChainConfigService {
interval: config.interval,
blockConfirmation: config.blockConfirmation,
maxBlockRange: config.maxBlockRange,
rpcConfirmation: config.rpcConfirmation,
isSolana,
} as ClassicChainConfig);
} as EvmChainConfig);
}
});
}
Expand Down Expand Up @@ -109,6 +111,7 @@ export class ChainConfigService {
provider: config.provider,
user: config.user,
password: config.password,
requireConfirmation: config.requireConfirmation,
authType,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { Web3Service } from '../../../web3/services/Web3Service';
import { ChainScanningService } from './ChainScanningService';
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';
import { SubmissionProcessingService } from './SubmissionProcessingService';
import { TransformService } from './TransformService';
import { RpcValidationStatusEnum } from '../../../../enums/RpcValidationStatusEnum';

@Injectable()
export class AddNewEventsAction {
Expand Down Expand Up @@ -64,10 +65,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 registerInstance = new web3.eth.Contract(deBridgeGateAbi as any, chainDetail.debridgeAddr);
// @ts-ignore
web3.eth.setProvider = registerInstance.setProvider;
Expand Down Expand Up @@ -104,6 +104,58 @@ export class AddNewEventsAction {
this.logger.error(`Error in transforming sent event to submission ${submissionId}: ${e.message}`);
}
});

if (chainDetail.rpcConfirmation) {
//validate that n rpc return the same submissions
try {
// if providers in config less amount that needed confirmation we dont need to check
if (chainDetail.providers.size() >= chainDetail.rpcConfirmation) {
const providersForConfirmation = chainDetail.providers.getAllProviders();
const rpcConfirmationSubmissions = await Promise.all(
providersForConfirmation.map(async provider => {
const web3ForConfirmation = await this.web3Service.createWeb3HttpProviderByProvider(provider, chainDetail.providers);
const registerInstanceConfirmation = new web3.eth.Contract(deBridgeGateAbi as any, chainDetail.debridgeAddr);
// @ts-ignore
web3ForConfirmation.eth.setProvider = registerInstanceConfirmation.setProvider;

let events = [];
try {
events = await this.getEvents(registerInstanceConfirmation, fromBlock, lastBlockOfPage);
} catch (e) {
this.logger.error(`Error in getting events: ${e.message}`);
}
return {
provider,
submissionIds: events.map(i => i.returnValues.submissionId),
};
}),
);
for (const submission of submissions) {
let confirmations = 0;
for (const rpcConfirmationSubmission of rpcConfirmationSubmissions) {
if (rpcConfirmationSubmission.submissionIds.includes(submission.submissionId)) {
confirmations++;
this.logger.log(`${rpcConfirmationSubmission.provider} returns correct ${submission.submissionId}`);
} else {
this.logger.log(`${rpcConfirmationSubmission.provider} returns incorrect ${submission.submissionId}`);
if (chainDetail.providers.getRequireConfirmation(rpcConfirmationSubmission.provider)) {
confirmations = 0;
break;
}
}
}

submission.rpcConfirmation =
confirmations >= chainDetail.rpcConfirmation ? RpcValidationStatusEnum.VALIDATED : RpcValidationStatusEnum.NEW;
}
} else {
this.logger.warn(`Require confirmation ${chainDetail.rpcConfirmation} but setted ${chainDetail.providers.size()} rpc`);
}
} catch (e) {
this.logger.error(`Error in rpc confirmations: ${e.message}`);
}
}

await this.chainProcessingService.process(submissions, chainId, lastBlockOfPage, web3);
}
}
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}`,
);
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UploadStatusEnum } from '../../../../enums/UploadStatusEnum';
import { SubmisionAssetsStatusEnum } from '../../../../enums/SubmisionAssetsStatusEnum';
import { ChainConfigService } from '../../config/services/ChainConfigService';
import { BundlrStatusEnum } from '../../../../enums/BundlrStatusEnum';
import { RpcValidationStatusEnum } from '../../../../enums/RpcValidationStatusEnum';

export class SolanaEvent extends EventFromTransaction {
slotNumber: number;
Expand Down Expand Up @@ -43,6 +44,7 @@ export class TransformService {
submission.decimalDenominator = transaction.decimalDenominator;
submission.assetsStatus = SubmisionAssetsStatusEnum.NEW;
submission.bundlrStatus = BundlrStatusEnum.NEW;
submission.rpcValidationStatus = RpcValidationStatusEnum.VALIDATED; //todo

return submission;
}
Expand All @@ -65,6 +67,7 @@ export class TransformService {
blockNumber: sendEvent.blockNumber,
nonce: parseInt(sendEvent.returnValues.nonce),
bundlrStatus: BundlrStatusEnum.NEW,
rpcValidationStatus: RpcValidationStatusEnum.NEW,
} as SubmissionEntity;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,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 @@ -45,12 +45,12 @@ export class StartScanningService implements OnModuleInit {
chainId: chainId,
latestBlock: 0,
latestSolanaTransaction: chainConfigSolana.lastTransaction,
network: chainConfigSolana.name
network: chainConfigSolana.name,
});
}
continue;
}
const chainConfigClassic = chainConfig as ClassicChainConfig;
const chainConfigClassic = chainConfig as EvmChainConfig;
if (chainConfigClassic.maxBlockRange <= 100) {
this.logger.error(`Cant up application maxBlockRange(${chainConfigClassic.maxBlockRange}) < 100`);
process.exit(1);
Expand Down Expand Up @@ -83,7 +83,7 @@ export class StartScanningService implements OnModuleInit {
if (chainDetail.isSolana) {
continue;
}
const chainConfigClassic = chainDetail as ClassicChainConfig;
const chainConfigClassic = chainDetail as EvmChainConfig;
await Promise.all(
chainConfigClassic.providers.getAllProviders().map(provider => {
return this.web3Service.validateChainId(chainConfigClassic.providers, provider);
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 { SolanaApiService } from '../../../external/solana_api/services/SolanaApiService';
import { BundlrStatusEnum } from '../../../../enums/BundlrStatusEnum';

Expand Down Expand Up @@ -59,7 +59,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 Down Expand Up @@ -193,7 +193,7 @@ export class CheckAssetsEventAction extends IAction {
}

private async getTokenInfo(nativeChainId: number, nativeTokenAddress: string) {
const tokenChainDetail = this.chainConfigService.get(nativeChainId) as ClassicChainConfig;
const tokenChainDetail = this.chainConfigService.get(nativeChainId) as EvmChainConfig;
const tokenWeb3 = await this.web3Service.web3HttpProvider(tokenChainDetail.providers);
const nativeTokenInstance = new tokenWeb3.eth.Contract(ERC20Abi as any, nativeTokenAddress);
const tokenName = await getTokenName(nativeTokenInstance, nativeTokenAddress, { logger: this.logger });
Expand Down
2 changes: 2 additions & 0 deletions debridge_node/src/modules/jobs/services/actions/SignAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Account } from 'web3-core';
import { ConfigService } from '@nestjs/config';
import { Web3Service } from '../../../web3/services/Web3Service';
import { readConfiguration } from '../../../../utils/readConfiguration';
import { RpcValidationStatusEnum } from '../../../../enums/RpcValidationStatusEnum';

//Simple action that sign submissionId and save signatures to DB
@Injectable()
Expand Down Expand Up @@ -42,6 +43,7 @@ export class SignAction extends IAction {
const submissions = await this.submissionsRepository.find({
where: {
status: SubmisionStatusEnum.NEW,
rpcValidationStatus: RpcValidationStatusEnum.VALIDATED,
},
});

Expand Down
59 changes: 33 additions & 26 deletions debridge_node/src/modules/web3/services/Web3Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,44 @@ export class Web3Service {
return new Web3();
}

async web3HttpProvider(chainProvider: ChainProvider): Promise<Web3Custom> {
for (const provider of [...chainProvider.getNotFailedProviders(), ...chainProvider.getFailedProviders()]) {
if (this.providersMap.has(provider)) {
const web3 = this.providersMap.get(provider);
const isWorking = await this.checkConnectionHttpProvider(web3);
if (isWorking) {
this.logger.verbose(`Old provider is working`);
return web3;
}
this.logger.error(`Old provider ${provider} is not working`);
async createWeb3HttpProviderByProvider(provider: string, chainProvider: ChainProvider) {
if (this.providersMap.has(provider)) {
const web3 = this.providersMap.get(provider);
const isWorking = await this.checkConnectionHttpProvider(web3);
if (isWorking) {
this.logger.verbose(`Old provider is working`);
return web3;
}
this.logger.error(`Old provider ${provider} is not working`);
}

const httpProvider = new Web3Custom.providers.HttpProvider(provider, {
timeout: this.web3Timeout,
keepAlive: true,
headers: chainProvider.getChainAuth(provider),
});
const httpProvider = new Web3Custom.providers.HttpProvider(provider, {
timeout: this.web3Timeout,
keepAlive: true,
headers: chainProvider.getChainAuth(provider),
});

const web3 = new Web3Custom(provider, httpProvider);
const isWorking = await this.checkConnectionHttpProvider(web3);
const web3 = new Web3Custom(provider, httpProvider);
const isWorking = await this.checkConnectionHttpProvider(web3);

if (!isWorking) {
chainProvider.setProviderStatus(provider, false);
continue;
}
if (!chainProvider.getProviderValidationStatus(provider)) {
await this.validateChainId(chainProvider, provider);
if (!isWorking) {
chainProvider.setProviderStatus(provider, false);
return;
}
if (!chainProvider.getProviderValidationStatus(provider)) {
await this.validateChainId(chainProvider, provider);
}
chainProvider.setProviderStatus(provider, true);
this.providersMap.set(provider, web3);
return web3;
}

async web3HttpProvider(chainProvider: ChainProvider): Promise<Web3Custom> {
for (const provider of [...chainProvider.getNotFailedProviders(), ...chainProvider.getFailedProviders()]) {
const web3Provider = await this.createWeb3HttpProviderByProvider(provider, chainProvider);
if (web3Provider) {
return web3Provider;
}
chainProvider.setProviderStatus(provider, true);
this.providersMap.set(provider, web3);
return web3;
}
const err = `Cann't connect to any provider ${chainProvider.getAllProviders()}`;
this.logger.error(err);
Expand Down