From d128caca49832566505e74a6782c373f540024d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aco=20S=CC=8Cmrkas?= Date: Thu, 30 Oct 2025 20:46:33 +0700 Subject: [PATCH] implemented handshake --- config/default.yaml | 13 ++++++++ package.json | 2 ++ src/config/config.ts | 44 +++++++++++++++++++++++++++ src/config/constants.ts | 2 ++ src/jobs/initScanner.ts | 7 +++++ src/types/config.ts | 3 +- src/utils/healthCheck.ts | 26 ++++++++++++++-- src/utils/networkConnectorManagers.ts | 38 +++++++++++++++++++++++ src/utils/scanner.ts | 41 +++++++++++++++++++++++-- 9 files changed, 171 insertions(+), 5 deletions(-) diff --git a/config/default.yaml b/config/default.yaml index 06ba3780..6371c9f0 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -73,6 +73,16 @@ binance: url: '' # rpc url timeout: 10 # rpc request timeout (in seconds) # authToken: # rpc auth token +handshake: + type: 'rpc' # options: rpc + initial: + height: -1 # initial height of scanning + interval: 180 # scanning interval (in seconds) + rpc: + timeout: 10 # rpc request timeout (in seconds) + url: '' # rpc url + # username: '' # rpc username for authentication required instances + # password: '' # rpc password for authentication required instances ergo: network: 'Mainnet' # ergo network type. testnet or mainnet type: 'node' # ergo scanner type. options: node, explorer @@ -153,6 +163,9 @@ healthCheck: binanceScanner: warnDifference: 160 # warning difference between existing and scanned blocks height criticalDifference: 640 # critical difference between existing and scanned blocks height + handshakeScanner: + warnDifference: 2 # warning difference between existing and scanned blocks height + criticalDifference: 10 # critical difference between existing and scanned blocks height permit: warnCommitmentCount: 4 # warning remaining permits for creating commitment criticalCommitmentCount: 0 # critical remaining permits for creating commitment diff --git a/package.json b/package.json index f111741e..7571c9e7 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,8 @@ "@rosen-bridge/evm-observation-extractor": "^5.2.1", "@rosen-bridge/evm-scanner": "^0.1.0", "@rosen-bridge/extended-typeorm": "0.2.1", + "@rosen-bridge/handshake-rpc-observation-extractor": "^0.1.0", + "@rosen-bridge/handshake-rpc-scanner": "^0.1.0", "@rosen-bridge/health-check": "^7.0.1", "@rosen-bridge/log-level-check": "^2.0.1", "@rosen-bridge/minimum-fee": "^2.3.0", diff --git a/src/config/config.ts b/src/config/config.ts index 4e9454d5..93a8ca08 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -20,6 +20,7 @@ const supportedNetworks: Array = [ Constants.DOGE_CHAIN_NAME, Constants.ETHEREUM_CHAIN_NAME, Constants.BINANCE_CHAIN_NAME, + Constants.HANDSHAKE_CHAIN_NAME, ]; interface ConfigType { @@ -30,6 +31,7 @@ interface ConfigType { ethereum: EthereumConfig; binance: BinanceConfig; doge: DogeConfig; + handshake: HandshakeConfig; general: Config; rosen: RosenConfig; database: DatabaseConfig; @@ -575,6 +577,37 @@ class BinanceConfig { } } +class HandshakeConfig { + type: string; + initialHeight: number; + interval: number; + rpc?: { + url: string; + timeout: number; + username?: string; + password?: string; + }; + + constructor(network: string) { + this.type = config.get('handshake.type'); + if (network === Constants.HANDSHAKE_CHAIN_NAME) { + this.initialHeight = getRequiredNumber('handshake.initial.height'); + this.interval = getRequiredNumber('handshake.interval'); + if (this.type == Constants.RPC_TYPE) { + const url = getRequiredString('handshake.rpc.url'); + const timeout = getRequiredNumber('handshake.rpc.timeout'); + const username = getOptionalString('handshake.rpc.username', undefined); + const password = getOptionalString('handshake.rpc.password', undefined); + this.rpc = { url, timeout, username, password }; + } else { + throw new Error( + `Improperly configured. handshake configuration type is invalid available choices are '${Constants.RPC_TYPE}'` + ); + } + } + } +} + class DatabaseConfig { type: string; path = ''; @@ -645,6 +678,8 @@ class HealthCheckConfig { ethereumScannerCriticalDiff: number; binanceScannerWarnDiff: number; binanceScannerCriticalDiff: number; + handshakeScannerWarnDiff: number; + handshakeScannerCriticalDiff: number; ergoNodeMaxHeightDiff: number; ergoNodeMaxBlockTime: number; ergoNodeMinPeerCount: number; @@ -712,6 +747,12 @@ class HealthCheckConfig { this.binanceScannerCriticalDiff = getRequiredNumber( 'healthCheck.binanceScanner.criticalDifference' ); + this.handshakeScannerWarnDiff = getRequiredNumber( + 'healthCheck.handshakeScanner.warnDifference' + ); + this.handshakeScannerCriticalDiff = getRequiredNumber( + 'healthCheck.handshakeScanner.criticalDifference' + ); this.permitWarnCommitmentCount = getRequiredNumber( 'healthCheck.permit.warnCommitmentCount' ); @@ -741,6 +782,7 @@ const getConfig = (): ConfigType => { const doge = new DogeConfig(general.networkWatcher); const ethereum = new EthereumConfig(general.networkWatcher); const binance = new BinanceConfig(general.networkWatcher); + const handshake = new HandshakeConfig(general.networkWatcher); const rosen = new RosenConfig( general.networkWatcher, general.networkType, @@ -756,6 +798,7 @@ const getConfig = (): ConfigType => { doge, ethereum, binance, + handshake, logger, general, rosen, @@ -777,4 +820,5 @@ export { EthereumConfig, BinanceConfig, DogeConfig, + HandshakeConfig, }; diff --git a/src/config/constants.ts b/src/config/constants.ts index 56ce76f7..9c83c4bc 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -18,6 +18,7 @@ export const BITCOIN_RUNES_CHAIN_NAME = 'bitcoin-runes'; export const DOGE_CHAIN_NAME = 'doge'; export const ETHEREUM_CHAIN_NAME = 'ethereum'; export const BINANCE_CHAIN_NAME = 'binance'; +export const HANDSHAKE_CHAIN_NAME = 'handshake'; export const ERGO_NATIVE_ASSET = 'erg'; export const ERGO_DECIMALS = 9; export const DEFAULT_API_LIMIT = 20; @@ -33,3 +34,4 @@ export const CARDANO_BLOCK_TIME = 20; export const ERGO_BLOCK_TIME = 120; export const ETHEREUM_BLOCK_TIME = 12; export const DOGE_BLOCK_TIME = 60; +export const HANDSHAKE_BLOCK_TIME = 600; diff --git a/src/jobs/initScanner.ts b/src/jobs/initScanner.ts index 092b5b84..daaf3fc0 100644 --- a/src/jobs/initScanner.ts +++ b/src/jobs/initScanner.ts @@ -15,6 +15,7 @@ const { doge: dogeConfig, ethereum: ethereumConfig, binance: binanceConfig, + handshake: handshakeConfig, } = allConfig; const logger = CallbackLoggerFactory.getInstance().getLogger(import.meta.url); @@ -80,6 +81,12 @@ export const scannerInit = () => { scanner.getObservationScanner() as EvmRpcScanner ).then(() => null); break; + case Constants.HANDSHAKE_CHAIN_NAME: + scanningJob( + handshakeConfig.interval, + scanner.getObservationScanner() as GeneralScanner + ).then(() => null); + break; case Constants.ERGO_CHAIN_NAME: break; default: diff --git a/src/types/config.ts b/src/types/config.ts index c3f9d47c..d5f58d6b 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -7,6 +7,7 @@ type NetworkType = | typeof Constants.BITCOIN_RUNES_CHAIN_NAME | typeof Constants.DOGE_CHAIN_NAME | typeof Constants.ETHEREUM_CHAIN_NAME - | typeof Constants.BINANCE_CHAIN_NAME; + | typeof Constants.BINANCE_CHAIN_NAME + | typeof Constants.HANDSHAKE_CHAIN_NAME; export { NetworkType }; diff --git a/src/utils/healthCheck.ts b/src/utils/healthCheck.ts index c7832458..02f39ccf 100644 --- a/src/utils/healthCheck.ts +++ b/src/utils/healthCheck.ts @@ -41,6 +41,8 @@ import { ETHEREUM_BLOCK_TIME, ETHEREUM_CHAIN_NAME, EXPLORER_TYPE, + HANDSHAKE_BLOCK_TIME, + HANDSHAKE_CHAIN_NAME, NODE_TYPE, OGMIOS_TYPE, } from '../config/constants'; @@ -223,38 +225,58 @@ class HealthCheckSingleton { } else { let chainName: string; let chainBlockTime: number; + let warnDiff: number; + let criticalDiff: number; switch (getConfig().general.networkWatcher) { case CARDANO_CHAIN_NAME: chainName = CARDANO_CHAIN_NAME; chainBlockTime = CARDANO_BLOCK_TIME; + warnDiff = getConfig().healthCheck.cardanoScannerWarnDiff; + criticalDiff = getConfig().healthCheck.cardanoScannerCriticalDiff; break; case BITCOIN_CHAIN_NAME: chainName = BITCOIN_CHAIN_NAME; chainBlockTime = BITCOIN_BLOCK_TIME; + warnDiff = getConfig().healthCheck.bitcoinScannerWarnDiff; + criticalDiff = getConfig().healthCheck.bitcoinScannerCriticalDiff; break; case BITCOIN_RUNES_CHAIN_NAME: chainName = BITCOIN_RUNES_CHAIN_NAME; chainBlockTime = BITCOIN_BLOCK_TIME; + warnDiff = getConfig().healthCheck.bitcoinScannerWarnDiff; + criticalDiff = getConfig().healthCheck.bitcoinScannerCriticalDiff; break; case DOGE_CHAIN_NAME: chainName = DOGE_CHAIN_NAME; chainBlockTime = DOGE_BLOCK_TIME; + warnDiff = getConfig().healthCheck.dogeScannerWarnDiff; + criticalDiff = getConfig().healthCheck.dogeScannerCriticalDiff; break; case ETHEREUM_CHAIN_NAME: chainName = ETHEREUM_CHAIN_NAME; chainBlockTime = ETHEREUM_BLOCK_TIME; + warnDiff = getConfig().healthCheck.ethereumScannerWarnDiff; + criticalDiff = getConfig().healthCheck.ethereumScannerCriticalDiff; break; case BINANCE_CHAIN_NAME: chainName = BINANCE_CHAIN_NAME; chainBlockTime = BINANCE_BLOCK_TIME; + warnDiff = getConfig().healthCheck.binanceScannerWarnDiff; + criticalDiff = getConfig().healthCheck.binanceScannerCriticalDiff; + break; + case HANDSHAKE_CHAIN_NAME: + chainName = HANDSHAKE_CHAIN_NAME; + chainBlockTime = HANDSHAKE_BLOCK_TIME; + warnDiff = getConfig().healthCheck.handshakeScannerWarnDiff; + criticalDiff = getConfig().healthCheck.handshakeScannerCriticalDiff; break; } scannerSyncCheck = new ScannerSyncHealthCheckParam( chainName!, this.observingNetworkLastBlock(scanner.getObservationScanner().name()), - getConfig().healthCheck.cardanoScannerWarnDiff, - getConfig().healthCheck.cardanoScannerCriticalDiff, + warnDiff!, + criticalDiff!, chainBlockTime! ); } diff --git a/src/utils/networkConnectorManagers.ts b/src/utils/networkConnectorManagers.ts index db8809ad..a3b0a9f4 100644 --- a/src/utils/networkConnectorManagers.ts +++ b/src/utils/networkConnectorManagers.ts @@ -15,6 +15,10 @@ import { EsploraNetwork, BitcoinEsploraTransaction, } from '@rosen-bridge/bitcoin-scanner'; +import { + HandshakeRpcTransaction, + HandshakeRpcNetwork, +} from '@rosen-bridge/handshake-rpc-scanner'; import { KoiosNetwork, BlockFrostNetwork, @@ -40,6 +44,8 @@ const bitcoinLogger = CallbackLoggerFactory.getInstance().getLogger('bitcoin-connector'); const dogeLogger = CallbackLoggerFactory.getInstance().getLogger('doge-connector'); +const handshakeLogger = + CallbackLoggerFactory.getInstance().getLogger('handshake-connector'); const cardanoKoiosLogger = CallbackLoggerFactory.getInstance().getLogger( 'cardano-koios-connector' ); @@ -282,3 +288,35 @@ export const createEvmNetworkConnectorManager = (chainName: string) => { return networkConnectorManager; }; + +/** + * Creates and configures a NetworkConnectorManager instance for Handshake RPC scanner + */ +export const createHandshakeRpcNetworkConnectorManager = () => { + const networkConnectorManager = + new NetworkConnectorManager( + new FailoverStrategy(), + handshakeLogger + ); + + if (config.handshake.rpc) { + networkConnectorManager.addConnector( + new HandshakeRpcNetwork( + config.handshake.rpc.url, + config.handshake.rpc.timeout * 1000, + config.handshake.rpc.username && config.handshake.rpc.password + ? { + username: config.handshake.rpc.username, + password: config.handshake.rpc.password, + } + : undefined + ) + ); + } else { + throw new Error( + 'Rpc configuration must be provided for Handshake Rpc network' + ); + } + + return networkConnectorManager; +}; diff --git a/src/utils/scanner.ts b/src/utils/scanner.ts index 57fca2ca..d1127668 100644 --- a/src/utils/scanner.ts +++ b/src/utils/scanner.ts @@ -5,12 +5,14 @@ import { BitcoinRpcScanner, DogeRpcScanner, } from '@rosen-bridge/bitcoin-scanner'; +import { HandshakeRpcScanner } from '@rosen-bridge/handshake-rpc-scanner'; import { BitcoinEsploraObservationExtractor, BitcoinRpcObservationExtractor, DogeEsploraObservationExtractor, DogeRpcObservationExtractor, } from '@rosen-bridge/bitcoin-observation-extractor'; +import { HandshakeRpcObservationExtractor } from '@rosen-bridge/handshake-rpc-observation-extractor'; import { CallbackLoggerFactory } from '@rosen-bridge/callback-logger'; import { ErgoObservationExtractor } from '@rosen-bridge/ergo-observation-extractor'; import { @@ -52,6 +54,7 @@ import { RosenConfig, DogeConfig, BitcoinRunesConfig, + HandshakeConfig, } from '../config/config'; import * as Constants from '../config/constants'; import { TokensConfig } from '../config/tokensConfig'; @@ -65,6 +68,7 @@ import { createEvmNetworkConnectorManager, createErgoNodeNetworkConnectorManager, createErgoExplorerNetworkConnectorManager, + createHandshakeRpcNetworkConnectorManager, } from './networkConnectorManagers'; /** @@ -109,7 +113,8 @@ class CreateScanner { | BitcoinRpcScanner | DogeEsploraScanner | DogeRpcScanner - | EvmRpcScanner; + | EvmRpcScanner + | HandshakeRpcScanner; private constructor() { // do nothing @@ -131,6 +136,7 @@ class CreateScanner { doge: dogeConfig, ethereum: ethereumConfig, binance: binanceConfig, + handshake: handshakeConfig, } = allConfig; await CreateScanner.instance.createErgoScanner(config, rosenConfig); @@ -172,6 +178,12 @@ class CreateScanner { rosenConfig ); break; + case Constants.HANDSHAKE_CHAIN_NAME: + await CreateScanner.instance.createHandshakeScanner( + handshakeConfig, + rosenConfig + ); + break; } if (!CreateScanner.instance.observationScanner) throw Error( @@ -213,7 +225,8 @@ class CreateScanner { | BitcoinRpcScanner | DogeEsploraScanner | DogeRpcScanner - | EvmRpcScanner => { + | EvmRpcScanner + | HandshakeRpcScanner => { if (!CreateScanner.instance) { throw new Error('Scanner is not initialized'); } @@ -539,6 +552,30 @@ class CreateScanner { } } }; + + private createHandshakeScanner = async ( + handshakeConfig: HandshakeConfig, + rosenConfig: RosenConfig + ) => { + if (!this.observationScanner) { + if (handshakeConfig.rpc) { + this.observationScanner = new HandshakeRpcScanner({ + dataSource, + initialHeight: handshakeConfig.initialHeight, + network: createHandshakeRpcNetworkConnectorManager(), + logger: loggers.observationScannerLogger, + }); + + const observationExtractor = new HandshakeRpcObservationExtractor( + rosenConfig.lockAddress, + dataSource, + TokensConfig.getInstance().getTokenMap(), + loggers.observationExtractorLogger + ); + this.observationScanner.registerExtractor(observationExtractor); + } + } + }; } export { CreateScanner };