diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index f5f80f430..15c20d5dd 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -64,6 +64,8 @@ class Chain extends AsyncEmitter { this.orphanMap = new BufferMap(); this.orphanPrev = new BufferMap(); + + this.getPrunedMap = new BufferMap(); } /** @@ -1371,7 +1373,16 @@ class Chain extends AsyncEmitter { } // Do we already have this block? - if (await this.hasEntry(hash)) { + const existingEntry = await this.getEntry(hash); + + // FOR EDUCATIONAL PURPOSES ONLY: save block without checking anything + if (existingEntry && this.getPrunedMap.has(hash)) { + block = block.toBlock(); + await this.db.save(existingEntry, block, new CoinView()); + return existingEntry; + } + + if (existingEntry) { this.logger.debug('Already have block: %h.', block.hash()); throw new VerifyError(block, 'duplicate', 'duplicate', 0); } @@ -1924,8 +1935,25 @@ class Chain extends AsyncEmitter { * @returns {Promise} - Returns {@link Block}. */ - getBlock(hash) { - return this.db.getBlock(hash); + async getBlock(hash) { + const block = await this.db.getBlock(hash); + if (block) { + return block; + } else { + this.logger.warning('Block not found, attempting to download'); + + // Ensure hash not height + hash = await this.db.getHash(hash); + + // FOR EDUCATIONAL PURPOSES ONLY: flag block for re-downloading + const wait = new Promise((resolve, reject) => { + this.getPrunedMap.set(hash, resolve); + }); + + await this.emitAsync('getprunedblock', hash); + await wait; + return this.db.getBlock(hash); + } } /** diff --git a/lib/net/pool.js b/lib/net/pool.js index 6541ce248..f96ea26e6 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -138,6 +138,16 @@ class Pool extends EventEmitter { this.handleBadOrphan('block', err, id); }); + this.chain.on('getprunedblock', async (hash) => { + // Find the first peer with a completed handshake + for (let peer = this.peers.head(); peer; peer = peer.next) { + if (!peer.handshake) + continue; + + await this.getBlock(peer, [hash]); + } + }); + if (this.mempool) { this.mempool.on('tx', (tx) => { this.emit('tx', tx); @@ -2184,6 +2194,14 @@ class Pool extends EventEmitter { throw err; } + // Someone was waiting for a pruned block to download + const resolve = this.chain.getPrunedMap.get(hash); + if (resolve) { + this.logger.warning('Received pruned block by special request'); + this.chain.getPrunedMap.delete(hash); + resolve(); + } + // Block was orphaned. if (!entry) { if (this.checkpoints) {