|
@ -61,6 +61,29 @@ def hash_header(header): |
|
|
return hash_encode(Hash(serialize_header(header).decode('hex'))) |
|
|
return hash_encode(Hash(serialize_header(header).decode('hex'))) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
blockchains = {} |
|
|
|
|
|
|
|
|
|
|
|
def read_blockchains(config): |
|
|
|
|
|
blockchains[0] = Blockchain(config, 'blockchain_headers') |
|
|
|
|
|
# fixme: sort |
|
|
|
|
|
for x in os.listdir(config.path): |
|
|
|
|
|
if x.startswith('fork_'): |
|
|
|
|
|
b = Blockchain(config, x) |
|
|
|
|
|
blockchains[b.checkpoint] = b |
|
|
|
|
|
return blockchains |
|
|
|
|
|
|
|
|
|
|
|
def get_blockchain(header): |
|
|
|
|
|
if type(header) is not dict: |
|
|
|
|
|
return False |
|
|
|
|
|
header_hash = hash_header(header) |
|
|
|
|
|
height = header.get('block_height') |
|
|
|
|
|
for b in blockchains.values(): |
|
|
|
|
|
if header_hash == b.get_hash(height): |
|
|
|
|
|
return b |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Blockchain(util.PrintError): |
|
|
class Blockchain(util.PrintError): |
|
|
|
|
|
|
|
|
'''Manages blockchain headers and their verification''' |
|
|
'''Manages blockchain headers and their verification''' |
|
@ -70,26 +93,28 @@ class Blockchain(util.PrintError): |
|
|
self.filename = filename |
|
|
self.filename = filename |
|
|
self.catch_up = None # interface catching up |
|
|
self.catch_up = None # interface catching up |
|
|
self.is_saved = True |
|
|
self.is_saved = True |
|
|
self.checkpoint = int(filename[16:]) if filename.startswith('blockchain_fork_') else 0 |
|
|
|
|
|
self.headers = [] |
|
|
self.headers = [] |
|
|
|
|
|
if filename == 'blockchain_headers': |
|
|
|
|
|
self.parent = None |
|
|
|
|
|
self.checkpoint = 0 |
|
|
|
|
|
elif filename.startswith('fork_'): |
|
|
|
|
|
self.parent = blockchains[int(filename.split('_')[1])] |
|
|
|
|
|
self.checkpoint = int(filename.split('_')[2]) |
|
|
|
|
|
else: |
|
|
|
|
|
raise BaseException('') |
|
|
self.set_local_height() |
|
|
self.set_local_height() |
|
|
|
|
|
|
|
|
def fork(parent, fork_point): |
|
|
def fork(parent, checkpoint): |
|
|
self = Blockchain(parent.config, parent.filename) |
|
|
filename = 'fork_%d_%d'%(parent.checkpoint, checkpoint) |
|
|
|
|
|
self = Blockchain(parent.config, filename) |
|
|
self.is_saved = False |
|
|
self.is_saved = False |
|
|
if parent.is_saved: |
|
|
self.parent = parent |
|
|
self.checkpoint = fork_point |
|
|
self.checkpoint = checkpoint |
|
|
else: |
|
|
|
|
|
if fork_point > parent.checkpoint: |
|
|
|
|
|
self.headers = parent.headers[0: fork_point - parent.checkpoint] |
|
|
|
|
|
else: |
|
|
|
|
|
self.headers = [] |
|
|
|
|
|
return self |
|
|
return self |
|
|
|
|
|
|
|
|
def height(self): |
|
|
def height(self): |
|
|
if self.headers: |
|
|
local = self.local_height if self.is_saved else len(self.headers) - 1 |
|
|
return self.checkpoint + len(self.headers) - 1 |
|
|
return self.checkpoint + local |
|
|
return self.local_height |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
@ -139,20 +164,15 @@ class Blockchain(util.PrintError): |
|
|
self.set_local_height() |
|
|
self.set_local_height() |
|
|
|
|
|
|
|
|
def save(self): |
|
|
def save(self): |
|
|
import shutil |
|
|
# recursively save parents if they have not been saved |
|
|
self.print_error("save fork") |
|
|
if self.parent and not self.parent.is_saved(): |
|
|
height = self.checkpoint |
|
|
self.parent.save() |
|
|
filename = "blockchain_fork_%d"%height |
|
|
open(self.path(), 'w+').close() |
|
|
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: |
|
|
for h in self.headers: |
|
|
self.write_header(h) |
|
|
self.write_header(h) |
|
|
self.headers = [] |
|
|
self.headers = [] |
|
|
|
|
|
self.is_saved = True |
|
|
|
|
|
self.print_error("saved", self.filename) |
|
|
|
|
|
|
|
|
def save_header(self, header): |
|
|
def save_header(self, header): |
|
|
height = header.get('block_height') |
|
|
height = header.get('block_height') |
|
@ -165,12 +185,12 @@ class Blockchain(util.PrintError): |
|
|
self.write_header(header) |
|
|
self.write_header(header) |
|
|
|
|
|
|
|
|
def write_header(self, header): |
|
|
def write_header(self, header): |
|
|
height = header.get('block_height') |
|
|
delta = header.get('block_height') - self.checkpoint |
|
|
data = serialize_header(header).decode('hex') |
|
|
data = serialize_header(header).decode('hex') |
|
|
assert len(data) == 80 |
|
|
assert len(data) == 80 |
|
|
filename = self.path() |
|
|
filename = self.path() |
|
|
with open(filename, 'rb+') as f: |
|
|
with open(filename, 'rb+') as f: |
|
|
f.seek(height * 80) |
|
|
f.seek(delta * 80) |
|
|
f.truncate() |
|
|
f.truncate() |
|
|
h = f.write(data) |
|
|
h = f.write(data) |
|
|
self.set_local_height() |
|
|
self.set_local_height() |
|
@ -180,25 +200,26 @@ class Blockchain(util.PrintError): |
|
|
name = self.path() |
|
|
name = self.path() |
|
|
if os.path.exists(name): |
|
|
if os.path.exists(name): |
|
|
h = os.path.getsize(name)/80 - 1 |
|
|
h = os.path.getsize(name)/80 - 1 |
|
|
if self.local_height != h: |
|
|
|
|
|
self.local_height = h |
|
|
self.local_height = h |
|
|
|
|
|
|
|
|
def read_header(self, height): |
|
|
def read_header(self, height): |
|
|
if not self.is_saved and height >= self.checkpoint: |
|
|
if height < self.checkpoint: |
|
|
i = height - self.checkpoint |
|
|
return self.parent.read_header(height) |
|
|
if i >= len(self.headers): |
|
|
delta = height - self.checkpoint |
|
|
|
|
|
if not self.is_saved: |
|
|
|
|
|
if delta >= len(self.headers): |
|
|
return None |
|
|
return None |
|
|
header = self.headers[i] |
|
|
header = self.headers[delta] |
|
|
assert header.get('block_height') == height |
|
|
assert header.get('block_height') == height |
|
|
return header |
|
|
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(height * 80) |
|
|
f.seek(delta * 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, height) |
|
|
h = deserialize_header(h, delta) |
|
|
return h |
|
|
return h |
|
|
|
|
|
|
|
|
def get_hash(self, height): |
|
|
def get_hash(self, height): |
|
|