mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-09-06 18:14:26 -05:00
Support for reorging genesis block
This commit is contained in:
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user