forked from LBRYCommunity/lbry-sdk
fixed reorg issue with headers
This commit is contained in:
parent
794eac0519
commit
ecebbc86c6
4 changed files with 28 additions and 30 deletions
|
@ -1,5 +1,4 @@
|
||||||
import logging
|
import logging
|
||||||
from unittest import skip
|
|
||||||
from torba.testcase import IntegrationTestCase
|
from torba.testcase import IntegrationTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,30 +7,23 @@ class BlockchainReorganizationTests(IntegrationTestCase):
|
||||||
VERBOSITY = logging.WARN
|
VERBOSITY = logging.WARN
|
||||||
|
|
||||||
async def test_reorg(self):
|
async def test_reorg(self):
|
||||||
|
# invalidate current block, move forward 2
|
||||||
self.assertEqual(self.ledger.headers.height, 200)
|
self.assertEqual(self.ledger.headers.height, 200)
|
||||||
|
self.assertEqual(
|
||||||
await self.blockchain.generate(1)
|
self.ledger.headers.hash(200).decode(),
|
||||||
await self.on_header(201)
|
await self.blockchain.get_block_hash(200)
|
||||||
|
)
|
||||||
|
await self.blockchain.invalidate_block(self.ledger.headers.hash(200).decode())
|
||||||
|
await self.blockchain.generate(2)
|
||||||
|
await self.ledger.on_header.where(lambda e: e.height == 201)
|
||||||
self.assertEqual(self.ledger.headers.height, 201)
|
self.assertEqual(self.ledger.headers.height, 201)
|
||||||
height = 201
|
self.assertEqual(
|
||||||
|
self.ledger.headers.hash(200).decode(),
|
||||||
|
await self.blockchain.get_block_hash(200)
|
||||||
|
)
|
||||||
|
|
||||||
# simple fork (rewind+advance to immediate best)
|
# invalidate current block, move forward 3
|
||||||
height = await self._simulate_reorg(height, 1, 1, 2)
|
await self.blockchain.invalidate_block(self.ledger.headers.hash(200).decode())
|
||||||
height = await self._simulate_reorg(height, 2, 1, 10)
|
await self.blockchain.generate(3)
|
||||||
height = await self._simulate_reorg(height, 4, 1, 3)
|
await self.ledger.on_header.where(lambda e: e.height == 202)
|
||||||
# lagged fork (rewind+batch catch up with immediate best)
|
self.assertEqual(self.ledger.headers.height, 202)
|
||||||
height = await self._simulate_reorg(height, 4, 2, 3)
|
|
||||||
await self._simulate_reorg(height, 4, 4, 3)
|
|
||||||
|
|
||||||
async def _simulate_reorg(self, height, rewind, winners, progress):
|
|
||||||
for i in range(rewind):
|
|
||||||
await self.blockchain.invalidateblock(self.ledger.headers.hash(height - i).decode())
|
|
||||||
await self.blockchain.generate(rewind + winners)
|
|
||||||
height = height + winners
|
|
||||||
await self.on_header(height)
|
|
||||||
for i in range(progress):
|
|
||||||
await self.blockchain.generate(1)
|
|
||||||
height += 1
|
|
||||||
await self.on_header(height)
|
|
||||||
self.assertEqual(height, self.ledger.headers.height)
|
|
||||||
return height
|
|
||||||
|
|
|
@ -39,7 +39,10 @@ class BaseHeaders:
|
||||||
|
|
||||||
async def open(self):
|
async def open(self):
|
||||||
if self.path != ':memory:':
|
if self.path != ':memory:':
|
||||||
self.io = open(self.path, 'a+b')
|
if not os.path.exists(self.path):
|
||||||
|
self.io = open(self.path, 'w+b')
|
||||||
|
else:
|
||||||
|
self.io = open(self.path, 'r+b')
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
self.io.close()
|
self.io.close()
|
||||||
|
@ -105,7 +108,7 @@ class BaseHeaders:
|
||||||
await loop.run_in_executor(None, self.validate_chunk, height, chunk)
|
await loop.run_in_executor(None, self.validate_chunk, height, chunk)
|
||||||
except InvalidHeader as e:
|
except InvalidHeader as e:
|
||||||
bail = True
|
bail = True
|
||||||
chunk = chunk[:(height-e.height+1)*self.header_size]
|
chunk = chunk[:(height-e.height)*self.header_size]
|
||||||
written = 0
|
written = 0
|
||||||
if chunk:
|
if chunk:
|
||||||
self.io.seek(height * self.header_size, os.SEEK_SET)
|
self.io.seek(height * self.header_size, os.SEEK_SET)
|
||||||
|
|
|
@ -364,9 +364,12 @@ class BlockchainNode:
|
||||||
self._block_expected += blocks
|
self._block_expected += blocks
|
||||||
return self._cli_cmnd('generate', str(blocks))
|
return self._cli_cmnd('generate', str(blocks))
|
||||||
|
|
||||||
def invalidateblock(self, blockhash):
|
def invalidate_block(self, blockhash):
|
||||||
return self._cli_cmnd('invalidateblock', blockhash)
|
return self._cli_cmnd('invalidateblock', blockhash)
|
||||||
|
|
||||||
|
def get_block_hash(self, block):
|
||||||
|
return self._cli_cmnd('getblockhash', str(block))
|
||||||
|
|
||||||
def get_raw_change_address(self):
|
def get_raw_change_address(self):
|
||||||
return self._cli_cmnd('getrawchangeaddress')
|
return self._cli_cmnd('getrawchangeaddress')
|
||||||
|
|
||||||
|
|
|
@ -457,13 +457,13 @@ class BlockProcessor:
|
||||||
hash_to_hex_str(self.tip),
|
hash_to_hex_str(self.tip),
|
||||||
self.height))
|
self.height))
|
||||||
self.tip = coin.header_prevhash(block.header)
|
self.tip = coin.header_prevhash(block.header)
|
||||||
self.backup_txs(block.transactions)
|
self.backup_txs(self.height, block.transactions)
|
||||||
self.height -= 1
|
self.height -= 1
|
||||||
self.db.tx_counts.pop()
|
self.db.tx_counts.pop()
|
||||||
|
|
||||||
self.logger.info('backed up to height {:,d}'.format(self.height))
|
self.logger.info('backed up to height {:,d}'.format(self.height))
|
||||||
|
|
||||||
def backup_txs(self, txs):
|
def backup_txs(self, height, txs):
|
||||||
# Prevout values, in order down the block (coinbase first if present)
|
# Prevout values, in order down the block (coinbase first if present)
|
||||||
# undo_info is in reverse block order
|
# undo_info is in reverse block order
|
||||||
undo_info = self.db.read_undo_info(self.height)
|
undo_info = self.db.read_undo_info(self.height)
|
||||||
|
|
Loading…
Reference in a new issue