UNPKG

bitcore-node

Version:

A blockchain indexing node with extended capabilities using bitcore

178 lines 7.54 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BitcoinBlockStorage = exports.BitcoinBlock = void 0; const Loggify_1 = require("../decorators/Loggify"); const logger_1 = __importDefault(require("../logger")); const baseBlock_1 = require("./baseBlock"); const coin_1 = require("./coin"); const events_1 = require("./events"); const transaction_1 = require("./transaction"); let BitcoinBlock = class BitcoinBlock extends baseBlock_1.BaseBlock { constructor(storage) { super(storage); } async addBlock(params) { const { block, chain, network } = params; const header = block.header.toObject(); const reorg = await this.handleReorg({ header, chain, network }); if (reorg) { return Promise.reject('reorg'); } return this.processBlock(params); } async processBlock(params) { const { chain, network, block, parentChain, forkHeight, initialSyncComplete } = params; const blockOp = await this.getBlockOp(params); const convertedBlock = blockOp.updateOne.update.$set; const { height, timeNormalized, time } = convertedBlock; const previousBlock = await this.collection.findOne({ hash: convertedBlock.previousBlockHash, chain, network }); await this.collection.bulkWrite([blockOp]); if (previousBlock) { await this.collection.updateOne({ chain, network, hash: previousBlock.hash }, { $set: { nextBlockHash: convertedBlock.hash } }); logger_1.default.debug('Updating previous block.nextBlockHash %o', convertedBlock.hash); } await transaction_1.TransactionStorage.batchImport({ txs: block.transactions, blockHash: convertedBlock.hash, blockTime: new Date(time), blockTimeNormalized: new Date(timeNormalized), height, chain, network, parentChain, forkHeight, initialSyncComplete }); if (initialSyncComplete) { events_1.EventStorage.signalBlock(convertedBlock); } await this.collection.updateOne({ hash: convertedBlock.hash, chain, network }, { $set: { processed: true } }); } async getBlockOp(params) { const { block, chain, network } = params; const header = block.header.toObject(); const blockTime = header.time * 1000; const previousBlock = await this.collection.findOne({ hash: header.prevHash, chain, network }); const blockTimeNormalized = (() => { const prevTime = previousBlock ? new Date(previousBlock.timeNormalized) : null; if (prevTime && blockTime <= prevTime.getTime()) { return prevTime.getTime() + 1; } else { return blockTime; } })(); const height = previousBlock?.height + 1 || params.initialHeight || 0; logger_1.default.debug('Setting blockheight: ' + height); const convertedBlock = { chain, network, hash: block.hash, height, version: header.version, nextBlockHash: '', previousBlockHash: header.prevHash, merkleRoot: header.merkleRoot, time: new Date(blockTime), timeNormalized: new Date(blockTimeNormalized), bits: header.bits, nonce: header.nonce, transactionCount: block.transactions.length, size: block.toBuffer().length, reward: block.transactions[0].outputAmount, processed: false }; return { updateOne: { filter: { hash: header.hash, chain, network }, update: { $set: convertedBlock }, upsert: true } }; } async handleReorg(params) { const { header, chain, network } = params; let localTip = await this.getLocalTip(params); if (header && localTip && localTip.hash === header.prevHash) { return false; } if (!localTip || localTip.height === 0) { return false; } if (header) { const prevBlock = await this.collection.findOne({ chain, network, hash: header.prevHash }); if (prevBlock) { localTip = prevBlock; } else { logger_1.default.error("Previous block isn't in the DB need to roll back until we have a block in common"); } logger_1.default.info(`Resetting tip to ${localTip.height - 1}, %o`, { chain, network }); } const reorgOps = [ this.collection.deleteMany({ chain, network, height: { $gte: localTip.height } }), transaction_1.TransactionStorage.collection.deleteMany({ chain, network, blockHeight: { $gte: localTip.height } }), coin_1.CoinStorage.collection.deleteMany({ chain, network, mintHeight: { $gte: localTip.height } }) ]; await Promise.all(reorgOps); await coin_1.CoinStorage.collection.updateMany({ chain, network, spentHeight: { $gte: localTip.height } }, { $set: { spentTxid: null, spentHeight: -2 /* SpentHeightIndicators.unspent */ } }); logger_1.default.debug('Removed data from above blockHeight: %o', localTip.height); return true; } _apiTransform(block, options) { const transform = { chain: block.chain, network: block.network, hash: block.hash, height: block.height, version: block.version, size: block.size, merkleRoot: block.merkleRoot, time: block.time, timeNormalized: block.timeNormalized, nonce: block.nonce, bits: block.bits, /* *difficulty: block.difficulty, */ /* *chainWork: block.chainWork, */ previousBlockHash: block.previousBlockHash, nextBlockHash: block.nextBlockHash, reward: block.reward, /* *isMainChain: block.mainChain, */ transactionCount: block.transactionCount /* *minedBy: BlockModel.getPoolInfo(block.minedBy) */ }; if (options && options.object) { return transform; } return JSON.stringify(transform); } }; exports.BitcoinBlock = BitcoinBlock; exports.BitcoinBlock = BitcoinBlock = __decorate([ Loggify_1.LoggifyClass ], BitcoinBlock); exports.BitcoinBlockStorage = new BitcoinBlock(); //# sourceMappingURL=block.js.map