def load_state(self) -> bool: try: self.pstate.prev_stake_validators_tracker = StakeValidatorsTracker.from_json( self.pstate.get_prev_stake_validators_tracker()) self.pstate.stake_validators_tracker = StakeValidatorsTracker.from_json( self.pstate.get_stake_validators_tracker()) block_number = self.pstate.get_state_version() block = Block.from_json(self.pstate.get_block(block_number)) self.blockchain.append(block) return True except Exception: return False
def test_add_future_sv(self): alice_xmss = get_alice_xmss() slave_xmss = XMSS(alice_xmss.height, alice_xmss.get_seed()) stake_validators_tracker = StakeValidatorsTracker.create() stake_transaction = StakeTransaction.create(10, alice_xmss, slave_xmss.pk(), b'1111') stake_validators_tracker.add_sv(100, stake_transaction, 1) total_stake_amount = stake_validators_tracker.get_total_stake_amount() self.assertNotEqual(100, total_stake_amount)
def test_add_sv(self): alice_xmss = get_alice_xmss() slave_xmss = XMSS(alice_xmss.height, alice_xmss.get_seed()) stake_validators_tracker = StakeValidatorsTracker.create() stake_transaction = StakeTransaction.create(0, alice_xmss, slave_xmss.pk(), b'1111') stake_validators_tracker.add_sv(100, stake_transaction, 1) balance = stake_validators_tracker.get_stake_balance(alice_xmss.get_address().encode()) self.assertEqual(100, balance) total_stake_amount = stake_validators_tracker.get_total_stake_amount() self.assertEqual(100, total_stake_amount) stake_validators_tracker.to_json()
def test_add_many_and_save(self): with set_wallet_dir("test_wallet"): with State() as state: self.assertIsNotNone(state) chain = Chain(state) alice_xmss = get_alice_xmss() staking_address = bytes(alice_xmss.get_address().encode()) with patch('qrl.core.config.dev.disk_writes_after_x_blocks'): qrl.core.config.dev.disk_writes_after_x_blocks = 4 prev = bytes() address_state_dict = dict() address_state_dict[staking_address] = AddressState.create( address=staking_address, nonce=0, balance=100, pubhashes=[]) for i in range(10): tmp_block1 = Block.create( staking_address=staking_address, block_number=i, reveal_hash=bytes(), prevblock_headerhash=prev, transactions=[], duplicate_transactions=OrderedDict(), vote=VoteMetadata(), signing_xmss=alice_xmss, nonce=address_state_dict[staking_address].nonce + 1) prev = tmp_block1.headerhash res = chain.add_block(tmp_block1, address_state_dict, StakeValidatorsTracker(), b'1001', alice_xmss) address_state_dict[staking_address].increase_nonce() address_state_dict[ staking_address].balance += tmp_block1.block_reward self.assertEqual( i, chain.height ) # FIXME: wrong name, it is not height but max_index self.assertTrue(res) print(qrl.core.config.dev.disk_writes_after_x_blocks)
def test_getStats(self): db_state = Mock(spec=State) db_state.stake_validators_tracker = StakeValidatorsTracker() db_state.total_coin_supply = MagicMock(return_value=1000) p2p_factory = Mock(spec=P2PFactory) p2p_factory.sync_state = SyncState() p2p_factory.connections = 23 p2p_factory.pos = Mock() p2p_factory.pos.stake = False buffered_chain = Mock(spec=BufferedChain) buffered_chain.height = 0 buffered_chain._chain = Mock() buffered_chain._chain.blockchain = [] buffered_chain.get_block = MagicMock(return_value=None) buffered_chain.state = db_state qrlnode = QRLNode(db_state) qrlnode.set_p2pfactory(p2p_factory) qrlnode.set_chain(buffered_chain) service = PublicAPIService(qrlnode) stats = service.GetStats(request=qrl_pb2.GetStatsReq, context=None) # self.assertEqual(__version__, stats.node_info.version) # FIXME self.assertEqual(qrl_pb2.NodeInfo.UNSYNCED, stats.node_info.state) self.assertEqual(23, stats.node_info.num_connections) # self.assertEqual("testnet", stats.node_info.network_id) # FIXME self.assertEqual(0, stats.epoch) self.assertEqual(0, stats.uptime_network) self.assertEqual(0, stats.stakers_count) self.assertEqual(0, stats.block_last_reward) self.assertEqual(0, stats.block_time_mean) self.assertEqual(0, stats.block_time_sd) self.assertEqual(105000000, stats.coins_total_supply) self.assertEqual(1000, stats.coins_emitted) self.assertEqual(0, stats.coins_atstake) logger.info(stats)
def test_last_block(self): with set_wallet_dir("test_wallet"): with State() as state: self.assertIsNotNone(state) chain = Chain(state) alice_xmss = get_alice_xmss() staking_address = bytes(alice_xmss.get_address().encode()) address_state_dict = dict() address_state_dict[staking_address] = AddressState.create( address=staking_address, nonce=0, balance=100, pubhashes=[], tokens=dict()) tmp_block1 = Block.create( staking_address=staking_address, block_number=0, reveal_hash=bytes(), prevblock_headerhash=bytes(), transactions=[], duplicate_transactions=OrderedDict(), vote=VoteMetadata(), signing_xmss=alice_xmss, nonce=address_state_dict[staking_address].nonce + 1) res = chain.add_block(tmp_block1, address_state_dict, StakeValidatorsTracker(), b'101', alice_xmss) address_state_dict[staking_address].increase_nonce() address_state_dict[ staking_address].balance += tmp_block1.block_reward self.assertTrue(res) self.assertEqual( 0, chain.height ) # FIXME: wrong name, it is not height but max_index last_block = chain.get_last_block() self.assertEqual(tmp_block1, last_block)
def test_create1(self): stake_validators_tracker = StakeValidatorsTracker.create() self.assertIsInstance(stake_validators_tracker, StakeValidatorsTracker)
def __init__(self): self._db = db.DB() # generate db object here # FIXME: Move to BufferedChain self.stake_validators_tracker = StakeValidatorsTracker()
class State: # FIXME: Rename to PersistentState # FIXME: Move blockchain caching/storage over here # FIXME: Improve key generation def __init__(self): self._db = db.DB() # generate db object here # FIXME: Move to BufferedChain self.stake_validators_tracker = StakeValidatorsTracker() def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if self._db is not None: del self._db self._db = None def put_epoch_seed(self, epoch_seed): try: self._db.put('epoch_seed', epoch_seed) except Exception as e: # FIXME: Review logger.exception(e) return False def get_epoch_seed(self): try: return self._db.get('epoch_seed') except Exception as e: # FIXME: Review logger.warning("get_epoch_seed: %s %s", type(e), e) return False def get_lattice_public_key(self, address): try: return set(self._db.get(b'lattice_' + address)) except KeyError: return set() except Exception as e: logger.exception(e) return False def put_lattice_public_key(self, lattice_public_key_txn): address = lattice_public_key_txn.txfrom lattice_public_keys = self.get_lattice_public_key(address) lattice_public_keys.add(lattice_public_key_txn.kyber_pk) self._db.put(b'lattice_' + address, list(lattice_public_keys)) ######################################### ######################################### ######################################### ######################################### ######################################### def uptodate(self, height): # check state db marker to current blockheight. # FIXME: Remove return height == self._blockheight() def _blockheight(self): # FIXME: Remove return self._db.get('blockheight') def _set_blockheight(self, height): # FIXME: Remove return self._db.put('blockheight', height) ######################################### ######################################### ######################################### ######################################### ######################################### def update_last_tx(self, block): if len(block.transactions) == 0: return last_txn = [] try: last_txn = self._db.get('last_txn') except: # noqa pass for protobuf_txn in block.transactions[-20:]: txn = Transaction.from_pbdata(protobuf_txn) if txn.subtype == qrl_pb2.Transaction.TRANSFER: last_txn.insert( 0, [txn.to_json(), block.block_number, block.timestamp]) del last_txn[20:] self._db.put('last_txn', last_txn) def get_last_txs(self): try: last_txn = self._db.get('last_txn') except: # noqa return [] txs = [] for tx_metadata in last_txn: tx_json, block_num, block_ts = tx_metadata tx = Transaction.from_json(tx_json) txs.append(tx) return txs ######################################### ######################################### ######################################### ######################################### ######################################### def update_address_tx_hashes(self, addr: bytes, new_txhash: bytes): txhash = self.get_address_tx_hashes(addr) txhash.append(new_txhash) # FIXME: Json does not support bytes directly | Temporary workaround tmp_hashes = [bin2hstr(item) for item in txhash] self._db.put(b'txn_' + addr, tmp_hashes) def update_stake_validators( self, stake_validators_tracker: StakeValidatorsTracker): self.stake_validators_tracker = stake_validators_tracker def get_address_tx_hashes(self, addr: bytes) -> List[bytes]: try: tx_hashes = self._db.get(b'txn_' + addr) except KeyError: tx_hashes = [] except Exception as e: logger.exception(e) tx_hashes = [] tx_hashes = [bytes(hstr2bin(item)) for item in tx_hashes] return tx_hashes ######################################### ######################################### ######################################### ######################################### ######################################### def get_txn_count(self, addr): try: return self._db.get((b'txn_count_' + addr)) except KeyError: pass except Exception as e: # FIXME: Review logger.error('Exception in get_txn_count') logger.exception(e) return 0 def increase_txn_count(self, addr: bytes): # FIXME: This should be transactional last_count = self.get_txn_count(addr) self._db.put(b'txn_count_' + addr, last_count + 1) ######################################### ######################################### ######################################### ######################################### ######################################### def update_tx_metadata(self, block): if len(block.transactions) == 0: return # FIXME: Inconsistency in the keys/types for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) if txn.subtype in (qrl_pb2.Transaction.TRANSFER, qrl_pb2.Transaction.COINBASE): self._db.put( bin2hstr(txn.txhash), [txn.to_json(), block.block_number, block.timestamp]) if txn.subtype == qrl_pb2.Transaction.TRANSFER: self.update_address_tx_hashes(txn.txfrom, txn.txhash) self.update_address_tx_hashes(txn.txto, txn.txhash) self.increase_txn_count(txn.txto) self.increase_txn_count(txn.txfrom) def get_tx_metadata(self, txhash: bytes): try: tx_metadata = self._db.get(bin2hstr(txhash)) except Exception: return None if tx_metadata is None: return None txn_json, block_number, _ = tx_metadata return Transaction.from_json(txn_json), block_number def update_vote_metadata(self, block): if len(block.transactions) == 0: return total_stake_amount = self.stake_validators_tracker.get_total_stake_amount( ) voted_weight = 0 # FIXME: Inconsistency in the keys/types for protobuf_txn in block.vote: vote = Transaction.from_pbdata(protobuf_txn) voted_weight += self.stake_validators_tracker.get_stake_balance( vote.txfrom) self._db.put(b'vote_' + str(block.block_number).encode(), [voted_weight, total_stake_amount]) def get_vote_metadata(self, blocknumber: int): try: return self._db.get(b'vote_' + str(blocknumber).encode()) except Exception: return None ######################################### ######################################### ######################################### ######################################### ######################################### def _get_address_state(self, address: bytes) -> AddressState: data = self._db.get_raw(address) if data is None: raise KeyError("{} not found".format(address)) pbdata = qrl_pb2.AddressState() pbdata.ParseFromString(bytes(data)) address_state = AddressState(pbdata) return address_state def _save_address_state(self, address_state: AddressState): data = address_state.pbdata.SerializeToString() self._db.put_raw(address_state.address, data) def get_address(self, address: bytes) -> AddressState: # FIXME: Avoid two calls to know if address is not recognized (merged with is used) try: return self._get_address_state(address) except KeyError: # FIXME: Check all cases where address is not found return AddressState.create( address=address, nonce=config.dev.default_nonce, balance=config.dev.default_account_balance, pubhashes=[]) def nonce(self, addr: bytes) -> int: return self.get_address(addr).nonce def balance(self, addr: bytes) -> int: return self.get_address(addr).balance def pubhash(self, addr: bytes): return self.get_address(addr).pubhashes def address_used(self, address: bytes): # FIXME: Probably obsolete try: return self._get_address_state(address) except KeyError: return False except Exception as e: # FIXME: Review logger.error('Exception in address_used') logger.exception(e) raise def return_all_addresses(self): addresses = [] for key, data in self._db.RangeIter(b'Q', b'Qz'): pbdata = qrl_pb2.AddressState() pbdata.ParseFromString(bytes(data)) address_state = AddressState(pbdata) addresses.append(address_state) return addresses def zero_all_addresses(self): for k, v in self._db.RangeIter(b'Q', b'Qz'): self._db.delete(k) logger.info('Reset Finished') self._set_blockheight(0) ######################################### ######################################### ######################################### ######################################### ######################################### def total_coin_supply(self): # FIXME: This is temporary code. NOT SCALABLE. It is easy to keep a global count all_addresses = self.return_all_addresses() coins = 0 for a in all_addresses: coins = coins + a.balance return coins