diff --git a/server/block_processor.py b/server/block_processor.py index 33d3936..d3ec0e0 100644 --- a/server/block_processor.py +++ b/server/block_processor.py @@ -241,7 +241,7 @@ class MemPool(LoggedClass): await asyncio.sleep(0) if initial and time.time() > next_log: - next_log = time.time() + 10 + next_log = time.time() + 20 self.logger.info('{:,d} done ({:d}%)' .format(n, int(n / len(new_txs) * 100))) @@ -518,17 +518,7 @@ class BlockProcessor(server.db.DB): self.wall_time += now - self.last_flush self.last_flush = now self.last_flush_tx_count = self.tx_count - state = { - 'genesis': self.coin.GENESIS_HASH, - 'height': self.db_height, - 'tx_count': self.db_tx_count, - 'tip': self.db_tip, - 'flush_count': self.flush_count, - 'utxo_flush_count': self.utxo_flush_count, - 'wall_time': self.wall_time, - 'first_sync': self.first_sync, - } - batch.put(b'state', repr(state).encode()) + self.write_state(batch) def assert_flushed(self): '''Asserts state is fully flushed.''' diff --git a/server/db.py b/server/db.py index fc01c31..d0d14a0 100644 --- a/server/db.py +++ b/server/db.py @@ -29,6 +29,8 @@ class DB(LoggedClass): it was shutdown uncleanly. ''' + VERSIONS = [0] + class MissingUTXOError(Exception): '''Raised if a mempool tx input UTXO couldn't be found.''' @@ -53,7 +55,7 @@ class DB(LoggedClass): else: self.logger.info('successfully opened {} database {}' .format(env.db_engine, db_name)) - self.init_state_from_db() + self.read_state() create = self.db_height == -1 self.headers_file = self.open_file('headers', create) @@ -70,7 +72,7 @@ class DB(LoggedClass): else: assert self.db_tx_count == 0 - def init_state_from_db(self): + def read_state(self): if self.db.is_new: self.db_height = -1 self.db_tx_count = 0 @@ -81,7 +83,15 @@ class DB(LoggedClass): self.first_sync = True else: state = self.db.get(b'state') - state = ast.literal_eval(state.decode()) + if state: + state = ast.literal_eval(state.decode()) + if not isinstance(state, dict): + raise self.DBError('failed reading state from DB') + db_version = state.get('db_version', 0) + if db_version not in self.VERSIONS: + raise self.DBError('your DB version is {} but this software ' + 'only handles versions {}' + .format(db_version, self.VERSIONS)) if state['genesis'] != self.coin.GENESIS_HASH: raise self.DBError('DB genesis hash {} does not match coin {}' .format(state['genesis_hash'], @@ -92,7 +102,22 @@ class DB(LoggedClass): self.flush_count = state['flush_count'] self.utxo_flush_count = state['utxo_flush_count'] self.wall_time = state['wall_time'] - self.first_sync = state.get('first_sync', True) + self.first_sync = state['first_sync'] + + def write_state(self, batch): + '''Write chain state to the batch.''' + state = { + 'genesis': self.coin.GENESIS_HASH, + 'height': self.db_height, + 'tx_count': self.db_tx_count, + 'tip': self.db_tip, + 'flush_count': self.flush_count, + 'utxo_flush_count': self.utxo_flush_count, + 'wall_time': self.wall_time, + 'first_sync': self.first_sync, + 'db_version': max(self.VERSIONS), + } + batch.put(b'state', repr(state).encode()) def open_file(self, filename, create=False): '''Open the file name. Return its handle.'''