blockchain: chain hierarchy based on most work, not length
This commit is contained in:
@@ -141,6 +141,11 @@ def read_blockchains(config: 'SimpleConfig'):
|
|||||||
def get_best_chain() -> 'Blockchain':
|
def get_best_chain() -> 'Blockchain':
|
||||||
return blockchains[constants.net.GENESIS]
|
return blockchains[constants.net.GENESIS]
|
||||||
|
|
||||||
|
# block hash -> chain work; up to and including that block
|
||||||
|
_CHAINWORK_CACHE = {
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000": 0, # virtual block at height -1
|
||||||
|
} # type: Dict[str, int]
|
||||||
|
|
||||||
|
|
||||||
class Blockchain(util.PrintError):
|
class Blockchain(util.PrintError):
|
||||||
"""
|
"""
|
||||||
@@ -319,10 +324,10 @@ class Blockchain(util.PrintError):
|
|||||||
they will be stored in different files."""
|
they will be stored in different files."""
|
||||||
if self.parent is None:
|
if self.parent is None:
|
||||||
return False
|
return False
|
||||||
parent_branch_size = self.parent.height() - self.forkpoint + 1
|
if self.parent.get_chainwork() >= self.get_chainwork():
|
||||||
if parent_branch_size >= self.size(): # FIXME most work, not length
|
|
||||||
return False
|
return False
|
||||||
self.print_error("swap", self.forkpoint, self.parent.forkpoint)
|
self.print_error("swap", self.forkpoint, self.parent.forkpoint)
|
||||||
|
parent_branch_size = self.parent.height() - self.forkpoint + 1
|
||||||
forkpoint = self.forkpoint # type: Optional[int]
|
forkpoint = self.forkpoint # type: Optional[int]
|
||||||
parent = self.parent # type: Optional[Blockchain]
|
parent = self.parent # type: Optional[Blockchain]
|
||||||
child_old_id = self.get_id()
|
child_old_id = self.get_id()
|
||||||
@@ -481,6 +486,40 @@ class Blockchain(util.PrintError):
|
|||||||
bitsBase >>= 8
|
bitsBase >>= 8
|
||||||
return bitsN << 24 | bitsBase
|
return bitsN << 24 | bitsBase
|
||||||
|
|
||||||
|
def chainwork_of_header_at_height(self, height: int) -> int:
|
||||||
|
"""work done by single header at given height"""
|
||||||
|
chunk_idx = height // 2016 - 1
|
||||||
|
target = self.get_target(chunk_idx)
|
||||||
|
work = ((2 ** 256 - target - 1) // (target + 1)) + 1
|
||||||
|
return work
|
||||||
|
|
||||||
|
@with_lock
|
||||||
|
def get_chainwork(self, height=None) -> int:
|
||||||
|
if height is None:
|
||||||
|
height = max(0, self.height())
|
||||||
|
if constants.net.TESTNET:
|
||||||
|
# On testnet/regtest, difficulty works somewhat different.
|
||||||
|
# It's out of scope to properly implement that.
|
||||||
|
return height
|
||||||
|
last_retarget = height // 2016 * 2016 - 1
|
||||||
|
cached_height = last_retarget
|
||||||
|
while _CHAINWORK_CACHE.get(self.get_hash(cached_height)) is None:
|
||||||
|
if cached_height <= -1:
|
||||||
|
break
|
||||||
|
cached_height -= 2016
|
||||||
|
assert cached_height >= -1, cached_height
|
||||||
|
running_total = _CHAINWORK_CACHE[self.get_hash(cached_height)]
|
||||||
|
while cached_height < last_retarget:
|
||||||
|
cached_height += 2016
|
||||||
|
work_in_single_header = self.chainwork_of_header_at_height(cached_height)
|
||||||
|
work_in_chunk = 2016 * work_in_single_header
|
||||||
|
running_total += work_in_chunk
|
||||||
|
_CHAINWORK_CACHE[self.get_hash(cached_height)] = running_total
|
||||||
|
cached_height += 2016
|
||||||
|
work_in_single_header = self.chainwork_of_header_at_height(cached_height)
|
||||||
|
work_in_last_partial_chunk = (height % 2016 + 1) * work_in_single_header
|
||||||
|
return running_total + work_in_last_partial_chunk
|
||||||
|
|
||||||
def can_connect(self, header: dict, check_height: bool=True) -> bool:
|
def can_connect(self, header: dict, check_height: bool=True) -> bool:
|
||||||
if header is None:
|
if header is None:
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user