bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
178 lines • 7.54 kB
JavaScript
;
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