@cityofzion/neo-js
Version:
Running NEO blockchain full node with Node.js and MongoDB.
170 lines (148 loc) • 4.95 kB
text/typescript
import { EventEmitter } from 'events'
import { Logger, LoggerOptions } from 'node-log-it'
import { merge } from 'lodash'
import { Mesh, MeshOptions } from './core/mesh'
import { Node, NodeOptions } from './core/node'
import { Api, ApiOptions } from './core/api'
import { Syncer, SyncerOptions } from './core/syncer'
import { MemoryStorage, MemoryStorageOptions } from './storages/memory-storage'
import { MongodbStorage, MongodbStorageOptions } from './storages/mongodb-storage'
import { BlockAnalyzer, BlockAnalyzerOptions } from './analyzers/block-analyzer'
import { EndpointValidator } from './validators/endpoint-validator'
import profiles from './common/profiles'
import C from './common/constants'
const version = require('../package.json').version // tslint:disable-line
const MODULE_NAME = 'Neo'
const DEFAULT_OPTIONS: NeoOptions = {
network: C.network.testnet,
enableSyncer: true,
enableBlockAnalyzer: false,
loggerOptions: {},
}
export interface NeoOptions {
network?: string
storageType?: string
endpoints?: object[]
enableSyncer?: boolean
enableBlockAnalyzer?: boolean
nodeOptions?: NodeOptions
meshOptions?: MeshOptions
storageOptions?: MemoryStorageOptions | MongodbStorageOptions
apiOptions?: ApiOptions
syncerOptions?: SyncerOptions
blockAnalyzerOptions?: BlockAnalyzerOptions
loggerOptions?: LoggerOptions
}
export class Neo extends EventEmitter {
mesh: Mesh
storage?: MemoryStorage | MongodbStorage
api: Api
syncer?: Syncer
blockAnalyzer?: BlockAnalyzer
private options: NeoOptions
private logger: Logger
constructor(options: NeoOptions = {}) {
super()
// Associate optional properties
this.options = merge({}, DEFAULT_OPTIONS, options)
this.validateOptionalParameters()
// Bootstrapping
this.logger = new Logger(MODULE_NAME, this.options.loggerOptions)
this.logger.info('Version:', Neo.VERSION)
this.mesh = this.getMesh()
this.storage = this.getStorage()
this.api = this.getApi()
this.syncer = this.getSyncer()
this.blockAnalyzer = this.getBlockAnalyzer()
this.logger.debug('constructor completes.')
}
static get VERSION(): string {
return version
}
static get UserAgent(): string {
return `NEO-JS:${Neo.VERSION}`
}
close() {
this.logger.debug('close triggered.')
if (this.syncer) {
this.syncer.close()
}
if (this.mesh) {
this.mesh.close()
}
if (this.storage) {
this.storage.close()
}
if (this.api) {
this.api.close()
}
if (this.blockAnalyzer) {
this.blockAnalyzer.close()
}
}
private validateOptionalParameters() {
// TODO
}
private getMesh(): Mesh {
this.logger.debug('getMesh triggered.')
const nodes = this.getNodes()
return new Mesh(nodes, this.options.meshOptions)
}
private getStorage(): MemoryStorage | MongodbStorage | undefined {
this.logger.debug('getStorage triggered.')
if (!this.options.storageType) {
// No storage
return undefined
} else if (this.options.storageType === C.storage.memory) {
return new MemoryStorage(this.options.storageOptions)
} else if (this.options.storageType === C.storage.mongodb) {
const mongoStorageOptions = merge({}, this.options.storageOptions, { userAgent: Neo.UserAgent })
return new MongodbStorage(mongoStorageOptions)
} else {
throw new Error(`Unknown storageType [${this.options.storageType}]`)
}
}
private getApi(): Api {
this.logger.debug('getApi triggered.')
return new Api(this.mesh, this.storage, this.options.apiOptions)
}
private getSyncer(): Syncer | undefined {
this.logger.debug('getSyncer triggered.')
if (this.options.enableSyncer) {
return new Syncer(this.mesh, this.storage, this.options.syncerOptions)
} else {
return undefined
}
}
private getBlockAnalyzer(): BlockAnalyzer | undefined {
this.logger.debug('getBlockAnalyzer triggered.')
if (this.options.enableBlockAnalyzer) {
return new BlockAnalyzer(this.storage, this.options.blockAnalyzerOptions)
} else {
return undefined
}
}
private getNodes(): Node[] {
this.logger.debug('getNodes triggered.')
// Fetch endpoints
let endpoints: object[] = []
if (this.options.endpoints) {
EndpointValidator.validateArray(this.options.endpoints)
endpoints = this.options.endpoints
} else if (this.options.network === C.network.testnet) {
endpoints = profiles.rpc.testnet
} else if (this.options.network === C.network.mainnet) {
endpoints = profiles.rpc.mainnet
} else {
throw new Error('Invalid network or provided endpoints.')
}
// Instantiate nodes
const nodes: Node[] = []
endpoints.forEach((item) => {
const endpoint = (item as any).endpoint
const node = new Node(endpoint, this.options.nodeOptions)
nodes.push(node)
})
return nodes
}
}