Support for reorging genesis block

This commit is contained in:
Mariano Sorgente
2021-01-12 17:00:50 -05:00
committed by Yostra
parent 30b2b8032f
commit 39bc19b643
3 changed files with 55 additions and 64 deletions
+11 -5
View File
@@ -1,10 +1,12 @@
from typing import Dict, Any
from typing import Dict, Any, Optional
from src.util.ints import uint32
def find_fork_point_in_chain(hash_to_block: Dict, block_1: Any, block_2: Any) -> uint32:
def find_fork_point_in_chain(hash_to_block: Dict, block_1: Any, block_2: Any) -> int:
"""Tries to find height where new chain (block_2) diverged from block_1 (assuming prev blocks
are all included in chain)"""
are all included in chain)
Returns -1 if chains have no common ancestor
"""
while block_2.height > 0 or block_1.height > 0:
if block_2.height > block_1.height:
block_2 = hash_to_block[block_2.prev_header_hash]
@@ -15,5 +17,9 @@ def find_fork_point_in_chain(hash_to_block: Dict, block_1: Any, block_2: Any) ->
return block_2.height
block_2 = hash_to_block[block_2.prev_header_hash]
block_1 = hash_to_block[block_1.prev_header_hash]
assert block_2 == block_1 # Genesis block is the same, genesis fork
return uint32(0)
if block_2 != block_1:
# All blocks are different
return -1
# First block is the same
return 0
+25 -17
View File
@@ -265,22 +265,25 @@ class Blockchain:
block.finished_slots is not None,
)
overflow = is_overflow_sub_block(self.constants, ips, required_iters)
prev_sb = self.sub_blocks[block.prev_header_hash]
if prev_sb.deficit == self.constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
# Prev sb must be an overflow sb
if overflow and block.finished_slots is None:
# Still overflowed, so we cannot decrease the deficit
deficit: uint8 = prev_sb.deficit
else:
# We have passed the first overflow, can decrease
deficit: uint8 = prev_sb.deficit - 1
elif prev_sb.deficit == 0:
if block.finished_slots is not None:
deficit = uint8(self.constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK)
else:
deficit = uint8(0)
if block.height == 0:
deficit = uint8(self.constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK) - 1
else:
deficit = prev_sb.deficit - 1
prev_sb = self.sub_blocks[block.prev_header_hash]
if prev_sb.deficit == self.constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK:
# Prev sb must be an overflow sb
if overflow and block.finished_slots is None:
# Still overflowed, so we cannot decrease the deficit
deficit: uint8 = prev_sb.deficit
else:
# We have passed the first overflow, can decrease
deficit: uint8 = prev_sb.deficit - 1
elif prev_sb.deficit == 0:
if block.finished_slots is not None:
deficit = uint8(self.constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK)
else:
deficit = uint8(0)
else:
deficit = prev_sb.deficit - 1
sub_block = full_block_to_sub_block_record(block, ips, required_iters, deficit)
@@ -309,19 +312,24 @@ class Blockchain:
assert self.get_peak() is not None
if sub_block.weight > self.get_peak().weight:
# Find the fork. if the block is just being appended, it will return the peak
fork_h: bytes32 = find_fork_point_in_chain(self.sub_blocks, sub_block, self.get_peak())
# If no blocks in common, returns -1, and reverts all blocks
fork_h: int = find_fork_point_in_chain(self.sub_blocks, sub_block, self.get_peak())
# Rollback to fork
await self.coin_store.rollback_to_block(fork_h)
# Collect all blocks from fork point to new peak
blocks_to_add: List[Tuple[FullBlock, SubBlockRecord]] = []
curr = sub_block.header_hash
while curr != self.height_to_hash[fork_h]:
while fork_h < 0 or curr != self.height_to_hash[uint32(fork_h)]:
fetched_block: Optional[FullBlock] = await self.block_store.get_block(curr)
fetched_sub_block: Optional[SubBlockRecord] = await self.block_store.get_sub_block(curr)
assert fetched_block is not None
assert fetched_sub_block is not None
blocks_to_add.append((fetched_block, fetched_sub_block))
if fetched_block.height == 0:
# Doing a full reorg, starting at height 0
break
curr = fetched_sub_block.prev_hash
for fetched_block, fetched_sub_block in reversed(blocks_to_add):
+19 -42
View File
@@ -18,9 +18,7 @@ class CoinStore:
cache_size: uint32
@classmethod
async def create(
cls, connection: aiosqlite.Connection, cache_size: uint32 = uint32(600000)
):
async def create(cls, connection: aiosqlite.Connection, cache_size: uint32 = uint32(600000)):
self = cls()
self.cache_size = cache_size
@@ -44,17 +42,11 @@ class CoinStore:
"CREATE INDEX IF NOT EXISTS coin_confirmed_index on coin_record(confirmed_index)"
)
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent_index on coin_record(spent_index)"
)
await self.coin_record_db.execute("CREATE INDEX IF NOT EXISTS coin_spent_index on coin_record(spent_index)")
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on coin_record(spent)"
)
await self.coin_record_db.execute("CREATE INDEX IF NOT EXISTS coin_spent on coin_record(spent)")
await self.coin_record_db.execute(
"CREATE INDEX IF NOT EXISTS coin_spent on coin_record(puzzle_hash)"
)
await self.coin_record_db.execute("CREATE INDEX IF NOT EXISTS coin_spent on coin_record(puzzle_hash)")
await self.coin_record_db.commit()
self.coin_record_cache = dict()
@@ -74,36 +66,26 @@ class CoinStore:
coinbase_coin = block.get_coinbase()
fees_coin = block.get_fees_coin()
coinbase_r: CoinRecord = CoinRecord(
coinbase_coin, block.height, uint32(0), False, True
)
coinbase_r: CoinRecord = CoinRecord(coinbase_coin, block.height, uint32(0), False, True)
fees_r: CoinRecord = CoinRecord(fees_coin, block.height, uint32(0), False, True)
await self._add_coin_record(coinbase_r)
await self._add_coin_record(fees_r)
# Checks DB and DiffStores for CoinRecord with coin_name and returns it
async def get_coin_record(
self, coin_name: bytes32
) -> Optional[CoinRecord]:
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]:
if coin_name.hex() in self.coin_record_cache:
return self.coin_record_cache[coin_name.hex()]
cursor = await self.coin_record_db.execute(
"SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)
)
cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),))
row = await cursor.fetchone()
await cursor.close()
if row is not None:
coin = Coin(
bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
)
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7])
return CoinRecord(coin, row[1], row[2], row[3], row[4])
return None
# Checks DB and DiffStores for CoinRecords with puzzle_hash and returns them
async def get_coin_records_by_puzzle_hash(
self, puzzle_hash: bytes32
) -> List[CoinRecord]:
async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[CoinRecord]:
coins = set()
cursor = await self.coin_record_db.execute(
"SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)
@@ -111,17 +93,18 @@ class CoinStore:
rows = await cursor.fetchall()
await cursor.close()
for row in rows:
coin = Coin(
bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
)
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7])
coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4]))
return list(coins)
async def rollback_to_block(self, block_index):
async def rollback_to_block(self, block_index: int):
"""
Note that block_index can be negative, in which case everything is rolled back
"""
# Update memory cache
delete_queue: bytes32 = []
for coin_name, coin_record in self.coin_record_cache.items():
if coin_record.spent_block_index > block_index:
if int(coin_record.spent_block_index) > block_index:
new_record = CoinRecord(
coin_record.coin,
coin_record.confirmed_block_index,
@@ -130,16 +113,14 @@ class CoinStore:
coin_record.coinbase,
)
self.coin_record_cache[coin_record.coin.name().hex()] = new_record
if coin_record.confirmed_block_index > block_index:
if int(coin_record.confirmed_block_index) > block_index:
delete_queue.append(coin_name)
for coin_name in delete_queue:
del self.coin_record_cache[coin_name]
# Delete from storage
c1 = await self.coin_record_db.execute(
"DELETE FROM coin_record WHERE confirmed_index>?", (block_index,)
)
c1 = await self.coin_record_db.execute("DELETE FROM coin_record WHERE confirmed_index>?", (block_index,))
await c1.close()
c2 = await self.coin_record_db.execute(
"UPDATE coin_record SET spent_index = 0, spent = 0 WHERE spent_index>?",
@@ -150,15 +131,11 @@ class CoinStore:
async def get_unspent_coin_records(self) -> List[CoinRecord]:
coins = set()
cursor = await self.coin_record_db.execute(
"SELECT * from coin_record WHERE spent=0"
)
cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE spent=0")
rows = await cursor.fetchall()
await cursor.close()
for row in rows:
coin = Coin(
bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
)
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7])
coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4]))
return list(coins)