diff --git a/config/default.yaml b/config/default.yaml index 4262662a..bb679754 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -77,6 +77,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 @@ -137,6 +147,9 @@ healthCheck: scanner: warnDifference: 5 # warning difference between existing and scanned blocks height criticalDifference: 20 # 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 83943b89..f5602727 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,8 @@ "@rosen-bridge/evm-observation-extractor": "^5.4.1", "@rosen-bridge/evm-scanner": "^0.1.4", "@rosen-bridge/extended-typeorm": "1.0.1", + "@rosen-bridge/handshake-rpc-observation-extractor": "^0.1.0", + "@rosen-bridge/handshake-rpc-scanner": "^0.1.0", "@rosen-bridge/health-check": "^8.0.0", "@rosen-bridge/json-bigint": "^1.1.0", "@rosen-bridge/log-level-check": "^3.0.0", diff --git a/src/config/config.ts b/src/config/config.ts index ce4aca88..a3892075 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; @@ -596,6 +598,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 = ''; @@ -706,6 +739,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.rosenConfigPath @@ -720,6 +754,7 @@ const getConfig = (): ConfigType => { doge, ethereum, binance, + handshake, logger, general, rosen, @@ -741,4 +776,5 @@ export { EthereumConfig, BinanceConfig, DogeConfig, + HandshakeConfig, }; diff --git a/src/config/constants.ts b/src/config/constants.ts index 8e8d956f..9f417422 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,5 +34,6 @@ 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; export const UNISAT_TYPE = 'unisat'; export const ORDISCAN_TYPE = 'ordiscan'; 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 1bdbff7c..faa3aa06 100644 --- a/src/utils/healthCheck.ts +++ b/src/utils/healthCheck.ts @@ -40,6 +40,8 @@ import { ETHEREUM_BLOCK_TIME, ETHEREUM_CHAIN_NAME, EXPLORER_TYPE, + HANDSHAKE_BLOCK_TIME, + HANDSHAKE_CHAIN_NAME, NODE_TYPE, OGMIOS_TYPE, } from '../config/constants'; @@ -248,6 +250,11 @@ class HealthCheckSingleton { chainBlockTime = BINANCE_BLOCK_TIME; updateInterval = getConfig().binance.interval; break; + case HANDSHAKE_CHAIN_NAME: + chainName = HANDSHAKE_CHAIN_NAME; + chainBlockTime = HANDSHAKE_BLOCK_TIME; + updateInterval = getConfig().handshake.interval; + break; } scannerSyncCheck = new ScannerSyncHealthCheckParam( 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 770df382..5e3a5aed 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 { @@ -55,6 +57,7 @@ import { RosenConfig, DogeConfig, BitcoinRunesConfig, + HandshakeConfig, } from '../config/config'; import * as Constants from '../config/constants'; import { TokensConfig } from '../config/tokensConfig'; @@ -68,6 +71,7 @@ import { createEvmNetworkConnectorManager, createErgoNodeNetworkConnectorManager, createErgoExplorerNetworkConnectorManager, + createHandshakeRpcNetworkConnectorManager, } from './networkConnectorManagers'; /** @@ -111,7 +115,8 @@ class CreateScanner { | BitcoinRpcScanner | DogeEsploraScanner | DogeRpcScanner - | EvmRpcScanner; + | EvmRpcScanner + | HandshakeRpcScanner; private constructor() { // do nothing @@ -133,6 +138,7 @@ class CreateScanner { doge: dogeConfig, ethereum: ethereumConfig, binance: binanceConfig, + handshake: handshakeConfig, } = allConfig; await CreateScanner.instance.createErgoScanner(config, rosenConfig); @@ -180,6 +186,12 @@ class CreateScanner { config.observationStoreRawData ); break; + case Constants.HANDSHAKE_CHAIN_NAME: + await CreateScanner.instance.createHandshakeScanner( + handshakeConfig, + rosenConfig + ); + break; } if (!CreateScanner.instance.observationScanner) throw Error( @@ -221,7 +233,8 @@ class CreateScanner { | BitcoinRpcScanner | DogeEsploraScanner | DogeRpcScanner - | EvmRpcScanner => { + | EvmRpcScanner + | HandshakeRpcScanner => { if (!CreateScanner.instance) { throw new Error('Scanner is not initialized'); } @@ -577,6 +590,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 };