mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
remove blockchain fork detection threshold
This commit is contained in:
parent
82c2c22a6e
commit
bf7deaa1fb
2 changed files with 72 additions and 54 deletions
|
@ -62,26 +62,30 @@ def hash_header(header):
|
||||||
|
|
||||||
|
|
||||||
class Blockchain(util.PrintError):
|
class Blockchain(util.PrintError):
|
||||||
|
|
||||||
'''Manages blockchain headers and their verification'''
|
'''Manages blockchain headers and their verification'''
|
||||||
def __init__(self, config, checkpoint):
|
|
||||||
|
def __init__(self, config, filename, fork_point):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.checkpoint = checkpoint
|
self.filename = filename
|
||||||
self.filename = 'blockchain_headers' if checkpoint == 0 else 'blockchain_fork_%d'%checkpoint
|
|
||||||
self.set_local_height()
|
|
||||||
self.catch_up = None # interface catching up
|
self.catch_up = None # interface catching up
|
||||||
|
if fork_point is None:
|
||||||
|
self.is_saved = True
|
||||||
|
self.checkpoint = int(filename[16:]) if filename.startswith('blockchain_fork_') else 0
|
||||||
|
else:
|
||||||
|
self.is_saved = False
|
||||||
|
self.checkpoint = fork_point
|
||||||
|
self.headers = []
|
||||||
|
self.set_local_height()
|
||||||
|
|
||||||
def height(self):
|
def height(self):
|
||||||
return self.local_height
|
return self.local_height + len(self.headers)
|
||||||
|
|
||||||
def verify_header(self, header, prev_header, bits, target):
|
def verify_header(self, header, prev_header, bits, target):
|
||||||
prev_hash = hash_header(prev_header)
|
prev_hash = hash_header(prev_header)
|
||||||
_hash = hash_header(header)
|
_hash = hash_header(header)
|
||||||
if prev_hash != header.get('prev_block_hash'):
|
if prev_hash != header.get('prev_block_hash'):
|
||||||
raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
|
raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
|
||||||
#if not self.pass_checkpoint(header):
|
|
||||||
# raise BaseException('failed checkpoint')
|
|
||||||
#if self.checkpoint_height == header.get('block_height'):
|
|
||||||
# self.print_error("validated checkpoint", self.checkpoint_height)
|
|
||||||
if bitcoin.TESTNET:
|
if bitcoin.TESTNET:
|
||||||
return
|
return
|
||||||
if bits != header.get('bits'):
|
if bits != header.get('bits'):
|
||||||
|
@ -115,24 +119,50 @@ class Blockchain(util.PrintError):
|
||||||
return os.path.join(d, self.filename)
|
return os.path.join(d, self.filename)
|
||||||
|
|
||||||
def save_chunk(self, index, chunk):
|
def save_chunk(self, index, chunk):
|
||||||
|
if not self.is_saved:
|
||||||
|
self.fork_and_save()
|
||||||
filename = self.path()
|
filename = self.path()
|
||||||
f = open(filename, 'rb+')
|
with open(filename, 'rb+') as f:
|
||||||
f.seek(index * 2016 * 80)
|
f.seek(index * 2016 * 80)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
h = f.write(chunk)
|
h = f.write(chunk)
|
||||||
f.close()
|
|
||||||
self.set_local_height()
|
self.set_local_height()
|
||||||
|
|
||||||
|
def fork_and_save(self):
|
||||||
|
import shutil
|
||||||
|
self.print_error("save fork")
|
||||||
|
height = self.checkpoint
|
||||||
|
filename = "blockchain_fork_%d"%height
|
||||||
|
new_path = os.path.join(util.get_headers_dir(self.config), filename)
|
||||||
|
shutil.copy(self.path(), new_path)
|
||||||
|
with open(new_path, 'rb+') as f:
|
||||||
|
f.seek((height) * 80)
|
||||||
|
f.truncate()
|
||||||
|
self.filename = filename
|
||||||
|
self.is_saved = True
|
||||||
|
for h in self.headers:
|
||||||
|
self.write_header(h)
|
||||||
|
self.headers = []
|
||||||
|
|
||||||
def save_header(self, header):
|
def save_header(self, header):
|
||||||
|
height = header.get('block_height')
|
||||||
|
if not self.is_saved:
|
||||||
|
assert height == self.checkpoint + len(self.headers) + 1
|
||||||
|
self.headers.append(header)
|
||||||
|
if len(self.headers) > 10:
|
||||||
|
self.fork_and_save()
|
||||||
|
return
|
||||||
|
self.write_header(header)
|
||||||
|
|
||||||
|
def write_header(self, header):
|
||||||
|
height = header.get('block_height')
|
||||||
data = serialize_header(header).decode('hex')
|
data = serialize_header(header).decode('hex')
|
||||||
assert len(data) == 80
|
assert len(data) == 80
|
||||||
height = header.get('block_height')
|
|
||||||
filename = self.path()
|
filename = self.path()
|
||||||
f = open(filename, 'rb+')
|
with open(filename, 'rb+') as f:
|
||||||
f.seek(height * 80)
|
f.seek(height * 80)
|
||||||
f.truncate()
|
f.truncate()
|
||||||
h = f.write(data)
|
h = f.write(data)
|
||||||
f.close()
|
|
||||||
self.set_local_height()
|
self.set_local_height()
|
||||||
|
|
||||||
def set_local_height(self):
|
def set_local_height(self):
|
||||||
|
@ -143,15 +173,22 @@ class Blockchain(util.PrintError):
|
||||||
if self.local_height != h:
|
if self.local_height != h:
|
||||||
self.local_height = h
|
self.local_height = h
|
||||||
|
|
||||||
def read_header(self, block_height):
|
def read_header(self, height):
|
||||||
|
if not self.is_saved and height >= self.checkpoint:
|
||||||
|
i = height - self.checkpoint
|
||||||
|
if i >= len(self.headers):
|
||||||
|
return None
|
||||||
|
header = self.headers[i]
|
||||||
|
assert header.get('block_height') == height
|
||||||
|
return header
|
||||||
name = self.path()
|
name = self.path()
|
||||||
if os.path.exists(name):
|
if os.path.exists(name):
|
||||||
f = open(name, 'rb')
|
f = open(name, 'rb')
|
||||||
f.seek(block_height * 80)
|
f.seek(height * 80)
|
||||||
h = f.read(80)
|
h = f.read(80)
|
||||||
f.close()
|
f.close()
|
||||||
if len(h) == 80:
|
if len(h) == 80:
|
||||||
h = deserialize_header(h, block_height)
|
h = deserialize_header(h, height)
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def get_hash(self, height):
|
def get_hash(self, height):
|
||||||
|
@ -173,17 +210,6 @@ class Blockchain(util.PrintError):
|
||||||
f.truncate()
|
f.truncate()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def fork(self, height):
|
|
||||||
import shutil
|
|
||||||
filename = "blockchain_fork_%d"%height
|
|
||||||
new_path = os.path.join(util.get_headers_dir(self.config), filename)
|
|
||||||
shutil.copy(self.path(), new_path)
|
|
||||||
with open(new_path, 'rb+') as f:
|
|
||||||
f.seek((height) * 80)
|
|
||||||
f.truncate()
|
|
||||||
f.close()
|
|
||||||
return filename
|
|
||||||
|
|
||||||
def get_target(self, index, chain=None):
|
def get_target(self, index, chain=None):
|
||||||
if bitcoin.TESTNET:
|
if bitcoin.TESTNET:
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
|
@ -205,13 +205,12 @@ class Network(util.DaemonThread):
|
||||||
config = {} # Do not use mutables as default values!
|
config = {} # Do not use mutables as default values!
|
||||||
util.DaemonThread.__init__(self)
|
util.DaemonThread.__init__(self)
|
||||||
self.config = SimpleConfig(config) if type(config) == type({}) else config
|
self.config = SimpleConfig(config) if type(config) == type({}) else config
|
||||||
self.num_server = 8 if not self.config.get('oneserver') else 0
|
self.num_server = 18 if not self.config.get('oneserver') else 0
|
||||||
self.blockchains = { 0:Blockchain(self.config, 0) }
|
self.blockchains = { 0:Blockchain(self.config, 'blockchain_headers', None) }
|
||||||
for x in os.listdir(self.config.path):
|
for x in os.listdir(self.config.path):
|
||||||
if x.startswith('blockchain_fork_'):
|
if x.startswith('blockchain_fork_'):
|
||||||
n = int(x[16:])
|
b = Blockchain(self.config, x, None)
|
||||||
b = Blockchain(self.config, n)
|
self.blockchains[b.checkpoint] = b
|
||||||
self.blockchains[n] = b
|
|
||||||
self.print_error("blockchains", self.blockchains.keys())
|
self.print_error("blockchains", self.blockchains.keys())
|
||||||
self.blockchain_index = config.get('blockchain_index', 0)
|
self.blockchain_index = config.get('blockchain_index', 0)
|
||||||
if self.blockchain_index not in self.blockchains.keys():
|
if self.blockchain_index not in self.blockchains.keys():
|
||||||
|
@ -864,23 +863,16 @@ class Network(util.DaemonThread):
|
||||||
if interface.bad != interface.good + 1:
|
if interface.bad != interface.good + 1:
|
||||||
next_height = (interface.bad + interface.good) // 2
|
next_height = (interface.bad + interface.good) // 2
|
||||||
else:
|
else:
|
||||||
interface.print_error("found connection at %d"% interface.good)
|
|
||||||
delta1 = interface.blockchain.height() - interface.good
|
delta1 = interface.blockchain.height() - interface.good
|
||||||
delta2 = interface.tip - interface.good
|
delta2 = interface.tip - interface.good
|
||||||
threshold = self.config.get('fork_threshold', 5)
|
interface.print_error("chain split detected at %d"%interface.good, delta1, delta2)
|
||||||
if delta1 > threshold and delta2 > threshold:
|
interface.blockchain = Blockchain(self.config, False, interface.bad)
|
||||||
interface.print_error("chain split detected: %d (%d %d)"% (interface.good, delta1, delta2))
|
|
||||||
interface.blockchain.fork(interface.bad)
|
|
||||||
interface.blockchain = Blockchain(self.config, interface.bad)
|
|
||||||
self.blockchains[interface.bad] = interface.blockchain
|
|
||||||
if interface.blockchain.catch_up is None:
|
|
||||||
interface.blockchain.catch_up = interface.server
|
interface.blockchain.catch_up = interface.server
|
||||||
|
self.blockchains[interface.bad] = interface.blockchain
|
||||||
interface.print_error("catching up")
|
interface.print_error("catching up")
|
||||||
interface.mode = 'catch_up'
|
interface.mode = 'catch_up'
|
||||||
next_height = interface.good
|
next_height = interface.good
|
||||||
else:
|
|
||||||
# todo: if current catch_up is too slow, queue others
|
|
||||||
next_height = None
|
|
||||||
elif interface.mode == 'catch_up':
|
elif interface.mode == 'catch_up':
|
||||||
if can_connect:
|
if can_connect:
|
||||||
interface.blockchain.save_header(header)
|
interface.blockchain.save_header(header)
|
||||||
|
|
Loading…
Add table
Reference in a new issue