Skip to content
This repository has been archived by the owner on May 19, 2023. It is now read-only.

Commit

Permalink
chore: fix pr comments
Browse files Browse the repository at this point in the history
  • Loading branch information
nduchak committed Sep 4, 2020
1 parent eb11972 commit 9836164
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 168 deletions.
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const services = {

export type AppOptions = { appResetCallBack: (...args: any) => void }

export async function appFactory (options: AppOptions): Promise<{ stop: () => void }> {
export async function startApp (options: AppOptions): Promise<{ stop: () => void }> {
const app: Application = express(feathers())

logger.verbose('Current configuration: ', config)
Expand Down
7 changes: 2 additions & 5 deletions src/blockchain/reorg-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ export class ReorgEmitterService implements Partial<ServiceMethods<any>> {

if (!this.timeoutStarted) {
setTimeout(() => {
if (this.emit) {
this.emit(REORG_OUT_OF_RANGE_EVENT_NAME, { contracts: this.reorgContract, lastProcessedBlockNumber: this.lastProcessedBlockNumber })
}
logger.warn(`Reorg outside of confirmation range happens on block number ${lastProcessedBlockNumber} for [${this.reorgContract}] contracts`)
this.emit!(REORG_OUT_OF_RANGE_EVENT_NAME, { contracts: this.reorgContract, lastProcessedBlockNumber: this.lastProcessedBlockNumber })
this.reorgContract = []
this.lastProcessedBlockNumber = 0
}, this.debounceTime)
Expand All @@ -42,7 +41,5 @@ export class ReorgEmitterService implements Partial<ServiceMethods<any>> {

this.reorgContract = [...this.reorgContract, contractName]
this.lastProcessedBlockNumber = lastProcessedBlockNumber

logger.info(`Reorg happens on block number ${lastProcessedBlockNumber} for ${contractName} contract`)
}
}
13 changes: 5 additions & 8 deletions src/cli/start.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import config from 'config'
import { flags } from '@oclif/command'

import { appFactory, services } from '../app'
import { startApp, services } from '../app'
import { loggingFactory } from '../logger'
import { Flags, Config, SupportedServices, isSupportedServices } from '../definitions'
import { BaseCLICommand, DbBackUpJob } from '../utils'
import { BaseCLICommand } from '../utils'
import DbBackUpJob from '../db-backup'
import { ethFactory } from '../blockchain'

const logger = loggingFactory('cli:start')
Expand All @@ -25,10 +26,6 @@ ${formattedServices}`
port: flags.integer({ char: 'p', description: 'port to attach the server to' }),
db: flags.string({ description: 'database connection URI', env: 'RIFM_DB' }),
provider: flags.string({ description: 'blockchain provider connection URI', env: 'RIFM_PROVIDER' }),
purge: flags.boolean({
char: 'u',
description: 'will purge services that should be lunched (eq. enable/disable is applied)'
}),
enable: flags.string({ char: 'e', multiple: true, description: 'enable specific service' }),
disable: flags.string({ char: 'd', multiple: true, description: 'disable specific service' })
}
Expand Down Expand Up @@ -109,7 +106,7 @@ ${formattedServices}`

// Promise that resolves when reset callback is called
const resetPromise = new Promise(resolve => {
appFactory({
startApp({
appResetCallBack: () => resolve()
}).then(value => {
// Lets save the function that stops the app
Expand All @@ -127,7 +124,7 @@ ${formattedServices}`
backUpJob.stop()

// Restore DB from backup
await backUpJob.restoreDb(() => undefined)
await backUpJob.restoreDb()

// Run pre-cache
await this.precache()
Expand Down
115 changes: 115 additions & 0 deletions src/db-backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import fs from 'fs'
import config from 'config'
import path from 'path'
import BigNumber from 'bignumber.js'
import { BlockHeader, Eth } from 'web3-eth'

import { AutoStartStopEventEmitter, NEW_BLOCK_EVENT_NAME } from './blockchain/new-block-emitters'
import { DbBackUpConfig } from './definitions'
import { getNewBlockEmitter } from './blockchain/utils'

export type BackUpEntry = { name: string, block: { hash: string, number: BigNumber } }

export function parseBackUps (backUpName: string): BackUpEntry {
const [block] = backUpName.split('.')[0].split('-')
const [hash, blockNumber] = block.split(':')

return {
name: backUpName,
block: { number: new BigNumber(blockNumber), hash }
}
}

export function getBackUps (): BackUpEntry[] {
const backupConfig = config.get<DbBackUpConfig>('dbBackUp')

const backups = fs.readdirSync(path.resolve(process.cwd(), `./${backupConfig.path}`))

if (backups.length) {
return backups
.map(parseBackUps)
.sort(
(a: Record<string, any>, b: Record<string, any>) =>
a.block.number.gt(b.block.number) ? -1 : 1
)
}

return []
}

export class DbBackUpJob {
readonly newBlockEmitter: AutoStartStopEventEmitter
readonly db: string
readonly eth: Eth
readonly backUpConfig: DbBackUpConfig

constructor (eth: Eth) {
if (!config.has('dbBackUp')) {
throw new Error('DB Backup config not exist')
}
this.backUpConfig = config.get<DbBackUpConfig>('dbBackUp')
this.db = config.get<string>('db')

if (!fs.existsSync(this.backUpConfig.path)) {
fs.mkdirSync(this.backUpConfig.path)
}

this.eth = eth
this.newBlockEmitter = getNewBlockEmitter(eth)
}

/**
* Back-up database if blocks condition met
* @return {Promise<void>}
* @param block
*/
private async backupDb (block: BlockHeader): Promise<void> {
const [lastBackUp, previousBackUp] = getBackUps()

if (!lastBackUp || new BigNumber(block.number).minus(this.backUpConfig.blocks).gte(lastBackUp.block.number)) {
// copy and rename current db
await fs.promises.copyFile(this.db, path.resolve(this.backUpConfig.path, `${block.hash}:${block.number}-${this.db}`))

// remove the oldest version
if (previousBackUp) {
await fs.promises.unlink(path.resolve(this.backUpConfig.path, previousBackUp.name))
}
}
}

/**
* Restore database backup
* @return {Promise<void>}
*/
public async restoreDb (): Promise<void> {
const backUps = getBackUps()
const [_, oldest] = backUps

if (backUps.length < 2) {
throw new Error('Should be two backups to be able to restore')
}

// Check if back up block hash exist after reorg
const block = await this.eth.getBlock(oldest.block.hash).catch(() => false)

if (!block) {
throw new Error('Invalid backup. Block Hash is not valid!')
}

// remove current db
await fs.promises.unlink(this.db)

// restore backup
await fs.promises.copyFile(path.resolve(this.backUpConfig.path, oldest.name), path.resolve(process.cwd(), this.db))
}

public run (): void {
this.newBlockEmitter.on(NEW_BLOCK_EVENT_NAME, this.backupDb.bind(this))
}

public stop (): void {
this.newBlockEmitter.stop()
}
}

export default DbBackUpJob
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { appFactory } from './app'
import { startApp } from './app'

(async function (): Promise<void> {
await appFactory({ appResetCallBack: () => { throw new Error('Reset callback not implemented') } })
await startApp({ appResetCallBack: () => { throw new Error('Reset callback not implemented') } })
})()
1 change: 1 addition & 0 deletions src/services/storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const storage: CachedService = {

return {
stop: () => {
confirmationService.removeAllListeners()
eventsEmitter.stop()
}
}
Expand Down
148 changes: 0 additions & 148 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,16 @@ import { Input, OutputFlags } from '@oclif/parser'
import { promisify } from 'util'
import config from 'config'
import fs from 'fs'
import path from 'path'
import { hexToAscii } from 'web3-utils'
import BigNumber from 'bignumber.js'
import { BlockHeader, Eth } from 'web3-eth'

import {
Application,
Config,
isSupportedServices,
Logger,
SupportedServices,
EventsEmitterOptions,
DbBackUpConfig
} from './definitions'
import { AutoStartStopEventEmitter, NEW_BLOCK_EVENT_NAME } from './blockchain/new-block-emitters'
import { getNewBlockEmitter } from './blockchain/utils'

const readFile = promisify(fs.readFile)

Expand Down Expand Up @@ -208,145 +202,3 @@ export abstract class BaseCLICommand extends Command {
return Promise.resolve()
}
}

type BackUpEntry = { name: string, block: { hash: string, number: BigNumber } }

function parseBackUps (backUpName: string): BackUpEntry {
const [block] = backUpName.split('.')[0].split('-')
const [hash, blockNumber] = block.split(':')

return {
name: backUpName,
block: { number: new BigNumber(blockNumber), hash }
}
}

function getBackUps (): BackUpEntry[] {
const backupConfig = config.get<DbBackUpConfig>('dbBackUp')

const backups = fs.readdirSync(path.resolve(__dirname, `../${backupConfig.path}`))

if (backups.length) {
return backups
.map(parseBackUps)
.sort(
(a: Record<string, any>, b: Record<string, any>) =>
a.block.number.gt(b.block.number) ? -1 : 1
)
}

return []
}

export class DbBackUpJob {
readonly newBlockEmitter: AutoStartStopEventEmitter
readonly db: string
readonly eth: Eth
readonly backUpConfig: DbBackUpConfig

constructor (eth: Eth) {
if (!config.has('dbBackUp')) {
throw new Error('DB Backup config not exist')
}
this.backUpConfig = config.get<DbBackUpConfig>('dbBackUp')
this.db = config.get<string>('db')

const eventEmittersConfirmations = this.getEventEmittersConfigs()
const invalidConfirmation = eventEmittersConfirmations.find(c => c.config.confirmations && c.config.confirmations > this.backUpConfig.blocks)

if (invalidConfirmation) {
throw new Error(`Invalid db backup configuration. Number of backup blocks should be greater then confirmation blocks for ${invalidConfirmation.name} service`)
}

if (!fs.existsSync(this.backUpConfig.path)) {
fs.mkdirSync(this.backUpConfig.path)
}

this.eth = eth
this.newBlockEmitter = getNewBlockEmitter(eth)
}

/**
* Back-up database if blocks condition met
* @return {Promise<void>}
* @param block
*/
private async backupDb (block: BlockHeader): Promise<void> {
const [lastBackUp, previousBackUp] = getBackUps()

if (!lastBackUp || new BigNumber(block.number).minus(this.backUpConfig.blocks).gte(lastBackUp.block.number)) {
// copy and rename current db
await fs.promises.copyFile(this.db, path.resolve(this.backUpConfig.path, `${block.hash}:${block.number}-${this.db}`))

// remove the oldest version
if (previousBackUp) {
await fs.promises.unlink(path.resolve(this.backUpConfig.path, previousBackUp.name))
}
}
}

/**
* Restore database backup
* @param {(message:any) => void} errorCallback
* @return {Promise<void>}
*/
public async restoreDb (errorCallback: (message: any) => void): Promise<void> {
const backUps = getBackUps()
const [_, oldest] = backUps

if (backUps.length < 2) {
errorCallback({ code: 1, message: 'Not enough backups' })
throw new Error('Should be two backups to be able to restore')
}

// Check if back up block hash exist after reorg
const block = await this.eth.getBlock(oldest.block.hash).catch(() => false)

if (!block) {
errorCallback({ code: 2, message: 'Invalid backup. Block Hash is not valid!' })
throw new Error('Invalid backup. Block Hash is not valid!')
}

// remove current db
await fs.promises.unlink(this.db)

// restore backup
await fs.promises.copyFile(path.resolve(this.backUpConfig.path, oldest.name), path.resolve(process.cwd(), this.db))
}

private getEventEmittersConfigs (): { config: EventsEmitterOptions, name: string }[] {
return Object.values(SupportedServices)
.reduce((acc: { config: EventsEmitterOptions, name: string }[], serviceName: string) => {
if (config.has(serviceName) && config.get(`${serviceName}.enabled`)) {
if (serviceName === SupportedServices.RNS) {
const rnsEmitters = ['owner', 'reverse', 'placement']
.reduce(
(acc2: any[], contract: string) => {
if (config.has(`${serviceName}.${contract}.eventsEmitter`)) {
const emitterConfig = config.get<EventsEmitterOptions>(`${serviceName}.${contract}.eventsEmitter`)
return [...acc2, { config: emitterConfig, name: `${serviceName}.${contract}` }]
}
return acc2
},
[]
)
return [...acc, ...rnsEmitters]
}

if (config.has(`${serviceName}.eventsEmitter`)) {
const emitterConfig = config.get<EventsEmitterOptions>(`${serviceName}.eventsEmitter`)
return [...acc, { config: emitterConfig, name: serviceName }]
}
}
return acc
}, [])
}

public run (): void {
this.newBlockEmitter.on(NEW_BLOCK_EVENT_NAME, this.backupDb.bind(this))
}

public stop (): void {
this.newBlockEmitter.stop()
}
}
Loading

0 comments on commit 9836164

Please sign in to comment.