def load(self, genesis_block): height = self.state.get_mainchain_height() if height == -1: self.state.put_block(genesis_block, None) block_number_mapping = qrl_pb2.BlockNumberMapping(headerhash=genesis_block.headerhash, prev_headerhash=genesis_block.prev_headerhash) self.state.put_block_number_mapping(genesis_block.block_number, block_number_mapping, None) parent_difficulty = StringToUInt256(str(config.dev.genesis_difficulty)) self.current_difficulty, _ = DifficultyTracker.get( measurement=config.dev.mining_setpoint_blocktime, parent_difficulty=parent_difficulty) block_metadata = BlockMetadata.create() block_metadata.set_orphan(False) block_metadata.set_block_difficulty(self.current_difficulty) block_metadata.set_cumulative_difficulty(self.current_difficulty) self.state.put_block_metadata(genesis_block.headerhash, block_metadata, None) addresses_state = dict() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address addresses_state[bytes_addr] = AddressState.get_default(bytes_addr) addresses_state[bytes_addr]._data.balance = genesis_balance.balance self.state.state_objects.update_current_state(addresses_state) self.state.state_objects.push(genesis_block.headerhash) else: self.last_block = self.get_block_by_number(height) self.current_difficulty = self.state.get_block_metadata(self.last_block.headerhash).block_difficulty
def _add_block_metadata(self, headerhash, block_timestamp, parent_headerhash, batch): block_metadata = self._state.get_block_metadata(headerhash) if not block_metadata: block_metadata = BlockMetadata.create() parent_metadata = self._state.get_block_metadata(parent_headerhash) parent_block_difficulty = parent_metadata.block_difficulty parent_cumulative_difficulty = parent_metadata.cumulative_difficulty block_metadata.update_last_headerhashes( parent_metadata.last_N_headerhashes, parent_headerhash) measurement = self._state.get_measurement(block_timestamp, parent_headerhash, parent_metadata) block_difficulty, _ = DifficultyTracker.get( measurement=measurement, parent_difficulty=parent_block_difficulty) block_cumulative_difficulty = StringToUInt256( str( int(UInt256ToString(block_difficulty)) + int(UInt256ToString(parent_cumulative_difficulty)))) block_metadata.set_block_difficulty(block_difficulty) block_metadata.set_cumulative_difficulty(block_cumulative_difficulty) parent_metadata.add_child_headerhash(headerhash) self._state.put_block_metadata(parent_headerhash, parent_metadata, batch) self._state.put_block_metadata(headerhash, block_metadata, batch) return block_metadata
def load(self, genesis_block): height = self.state.get_mainchain_height() if height == -1: self.state.put_block(genesis_block, None) block_number_mapping = qrl_pb2.BlockNumberMapping(headerhash=genesis_block.headerhash, prev_headerhash=genesis_block.prev_headerhash) self.state.put_block_number_mapping(genesis_block.block_number, block_number_mapping, None) parent_difficulty = StringToUInt256(str(config.dev.genesis_difficulty)) self.current_difficulty, _ = self._difficulty_tracker.get( measurement=config.dev.mining_setpoint_blocktime, parent_difficulty=parent_difficulty) block_metadata = BlockMetadata.create() block_metadata.set_orphan(False) block_metadata.set_block_difficulty(self.current_difficulty) block_metadata.set_cumulative_difficulty(self.current_difficulty) self.state.put_block_metadata(genesis_block.headerhash, block_metadata, None) addresses_state = dict() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address.encode() addresses_state[bytes_addr] = AddressState.get_default(bytes_addr) addresses_state[bytes_addr]._data.balance = genesis_balance.balance self.state.state_objects.update_current_state(addresses_state) self.state.state_objects.push(genesis_block.headerhash) else: self.last_block = self.get_block_by_number(height) self.current_difficulty = self.state.get_block_metadata(self.last_block.headerhash).block_difficulty
def test_GetBlockMiningCompatible(self): p2p_factory = Mock(spec=P2PFactory) p2p_factory.sync_state = SyncState() p2p_factory.num_connections = 23 p2p_factory.pow = Mock() chain_manager = Mock(spec=ChainManager) chain_manager.height = 0 chain_manager.get_last_block = MagicMock(return_value=Block()) qrlnode = QRLNode(mining_address=b'') qrlnode.set_chain_manager(chain_manager) qrlnode._p2pfactory = p2p_factory qrlnode._pow = p2p_factory.pow block_header = BlockHeader.create(blocknumber=10, prev_headerhash=sha256(b'prevblock'), prev_timestamp=1234567890, hashedtransactions=sha256(b'tx1'), fee_reward=1) qrlnode.get_blockheader_and_metadata = MagicMock( return_value=[block_header, BlockMetadata()]) service = MiningAPIService(qrlnode) req = qrlmining_pb2.GetBlockMiningCompatibleReq(height=10) answer = service.GetBlockMiningCompatible(request=req, context=None) self.assertEqual(10, answer.blockheader.block_number) self.assertEqual(1, answer.blockheader.reward_fee)
def test_put_block_metadata(self): with set_qrl_dir('no_data'): with State() as state: block_metadata = BlockMetadata.create() block_metadata.update_last_headerhashes([b'test1', b'test2'], b'test3') state.put_block_metadata(b'block_headerhash', block_metadata, None) state.put_block_metadata(b'block_headerhash2', BlockMetadata.create(), None) self.assertEqual(state.get_block_metadata(b'block_headerhash').to_json(), block_metadata.to_json()) expected_json = b'{\n "blockDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",\n ' \ b'"cumulativeDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="\n}' self.assertEqual(state.get_block_metadata(b'block_headerhash2').to_json(), expected_json)
def gen_blocks(block_count, state, miner_address): blocks = [] block = None with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 addresses_state = dict() for i in range(0, block_count): if i == 0: block = GenesisBlock() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address addresses_state[bytes_addr] = AddressState.get_default( bytes_addr) addresses_state[ bytes_addr]._data.balance = genesis_balance.balance else: block = Block.create(block_number=i, prev_headerhash=block.headerhash, prev_timestamp=block.timestamp, transactions=[], miner_address=miner_address) addresses_set = state.prepare_address_list(block) for address in addresses_set: addresses_state[address] = state.get_address_state(address) for tx_protobuf in block.transactions: tx = Transaction.from_pbdata(tx_protobuf) tx.apply_state_changes(addresses_state) block.set_nonces(10, 0) blocks.append(block) metadata = BlockMetadata() metadata.set_block_difficulty(StringToUInt256('256')) state.put_block_metadata(block.headerhash, metadata, None) state.put_block(block, None) bm = qrl_pb2.BlockNumberMapping( headerhash=block.headerhash, prev_headerhash=block.prev_headerhash) state.put_block_number_mapping(block.block_number, bm, None) state.update_mainchain_height(block.block_number, None) state.put_addresses_state(addresses_state) return blocks
def add_block_metadata(self, headerhash, block_timestamp, parent_headerhash, batch): block_metadata = self.state.get_block_metadata(headerhash) if not block_metadata: block_metadata = BlockMetadata.create() parent_metadata = self.state.get_block_metadata(parent_headerhash) block_difficulty = (0, ) * 32 # 32 bytes to represent 256 bit of 0 block_cumulative_difficulty = ( 0, ) * 32 # 32 bytes to represent 256 bit of 0 if not parent_metadata: parent_metadata = BlockMetadata.create() else: parent_block = self.state.get_block(parent_headerhash) if parent_block: parent_block_difficulty = parent_metadata.block_difficulty parent_cumulative_difficulty = parent_metadata.cumulative_difficulty if not parent_metadata.is_orphan: block_metadata.update_last_headerhashes( parent_metadata.last_N_headerhashes, parent_headerhash) measurement = self.state.get_measurement( block_timestamp, parent_headerhash, parent_metadata) block_difficulty, _ = DifficultyTracker.get( measurement=measurement, parent_difficulty=parent_block_difficulty) block_cumulative_difficulty = StringToUInt256( str( int(UInt256ToString(block_difficulty)) + int(UInt256ToString(parent_cumulative_difficulty))) ) block_metadata.set_orphan(parent_metadata.is_orphan) block_metadata.set_block_difficulty(block_difficulty) block_metadata.set_cumulative_difficulty(block_cumulative_difficulty) parent_metadata.add_child_headerhash(headerhash) self.state.put_block_metadata(parent_headerhash, parent_metadata, batch) self.state.put_block_metadata(headerhash, block_metadata, batch) # Call once to populate the cache self.state.get_block_datapoint(headerhash)
def test_put_block_metadata(self): with set_qrl_dir('no_data'): with State() as state: block_metadata = BlockMetadata.create(False) block_metadata.update_last_headerhashes([b'test1', b'test2'], b'test3') state.put_block_metadata(b'block_headerhash', block_metadata, None) state.put_block_metadata(b'block_headerhash2', BlockMetadata(), None) self.assertEqual( state.get_block_metadata(b'block_headerhash').to_json(), block_metadata.to_json()) self.assertEqual( state.get_block_metadata(b'block_headerhash2').to_json(), b'{}')
def test_get_block_metadata(self): with set_qrl_dir('no_data'): with State() as state: self.assertIsNone(state.get_block_metadata(b'test1')) state.put_block_metadata(b'block_headerhash2', BlockMetadata(), None) self.assertEqual( state.get_block_metadata(b'block_headerhash2').to_json(), b'{}')
def test_get_measurement(self): def block(headerhash): nth_block = Block() if headerhash == b'test_block_1': nth_block.blockheader._data.timestamp_seconds = 50000 elif headerhash == b'test_block_2': nth_block.blockheader._data.timestamp_seconds = 80000 elif headerhash == b'test_block_3': nth_block.blockheader._data.timestamp_seconds = 90000 return nth_block with set_qrl_dir('no_data'): with State() as state: parent_metadata = BlockMetadata.create(block_difficulty=b'\x00' * 32, cumulative_difficulty=b'\x00' * 32, child_headerhashes=[]) measurement = state.get_measurement(block_timestamp=100000, parent_headerhash=b'', parent_metadata=parent_metadata) # Test Case, when count_headerhashes equals 0 self.assertEqual(measurement, config.dev.mining_setpoint_blocktime) state.get_block = MagicMock(side_effect=block) parent_metadata.update_last_headerhashes([], b'test_block_1') measurement = state.get_measurement(block_timestamp=100000, parent_headerhash=b'test_block_1', parent_metadata=parent_metadata) # Test Case, when count_headerhashes equals 1 self.assertEqual(measurement, (100000 - 50000 + config.dev.mining_setpoint_blocktime) // 2) parent_metadata.update_last_headerhashes([b'test_block_1'], b'test_block_2') measurement = state.get_measurement(block_timestamp=100000, parent_headerhash=b'test_block_2', parent_metadata=parent_metadata) # Test Case, when count_headerhashes is greater than 1 # but less than config.dev.N_measurement self.assertEqual(measurement, (100000 - 80000 + config.dev.mining_setpoint_blocktime) // 2) parent_metadata.update_last_headerhashes([b'test_block_3'] * config.dev.N_measurement, b'test_block_2') measurement = state.get_measurement(block_timestamp=100000, parent_headerhash=b'test_block_2', parent_metadata=parent_metadata) # Test Case, when count_headerhashes is greater than config.dev.N_measurement self.assertEqual(measurement, (100000 - 90000) // config.dev.N_measurement)
def load(self, genesis_block): height = self.state.get_mainchain_height() if height == -1: self.state.put_block(genesis_block, None) block_number_mapping = qrl_pb2.BlockNumberMapping(headerhash=genesis_block.headerhash, prev_headerhash=genesis_block.prev_headerhash) self.state.put_block_number_mapping(genesis_block.block_number, block_number_mapping, None) parent_difficulty = StringToUInt256(str(config.dev.genesis_difficulty)) self.current_difficulty, _ = DifficultyTracker.get( measurement=config.dev.mining_setpoint_blocktime, parent_difficulty=parent_difficulty) block_metadata = BlockMetadata.create() block_metadata.set_orphan(False) block_metadata.set_block_difficulty(self.current_difficulty) block_metadata.set_cumulative_difficulty(self.current_difficulty) self.state.put_block_metadata(genesis_block.headerhash, block_metadata, None) addresses_state = dict() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address addresses_state[bytes_addr] = AddressState.get_default(bytes_addr) addresses_state[bytes_addr]._data.balance = genesis_balance.balance for tx_idx in range(1, len(genesis_block.transactions)): tx = Transaction.from_pbdata(genesis_block.transactions[tx_idx]) for addr in tx.addrs_to: addresses_state[addr] = AddressState.get_default(addr) coinbase_tx = Transaction.from_pbdata(genesis_block.transactions[0]) if not isinstance(coinbase_tx, CoinBase): return False addresses_state[coinbase_tx.addr_to] = AddressState.get_default(coinbase_tx.addr_to) if not coinbase_tx.validate_extended(): return False coinbase_tx.apply_on_state(addresses_state) for tx_idx in range(1, len(genesis_block.transactions)): tx = Transaction.from_pbdata(genesis_block.transactions[tx_idx]) tx.apply_on_state(addresses_state) self.state.state_objects.update_current_state(addresses_state) self.state.state_objects.update_tx_metadata(genesis_block, None) self.state.state_objects.push(genesis_block.headerhash) else: self.last_block = self.get_block_by_number(height) self.current_difficulty = self.state.get_block_metadata(self.last_block.headerhash).block_difficulty
def test_block_metadata(self): with set_data_dir('no_data'): with State() as state: alice_xmss = get_alice_xmss() blocks = gen_blocks(20, state, alice_xmss.address) for block in blocks: state.put_block_metadata(block.headerhash, BlockMetadata(), None) for block in blocks: self.assertEqual(state.get_block_metadata(block.headerhash).to_json(), b'{}')
def get_block_metadata(self, header_hash: bytes) -> Optional[BlockMetadata]: try: json_data = self._db.get_raw(b'metadata_' + bin2hstr(header_hash).encode()) return BlockMetadata.from_json(json_data) except KeyError: logger.debug('[get_block_metadata] Block header_hash %s not found', b'metadata_' + bin2hstr(header_hash).encode()) except Exception as e: logger.error('[get_block_metadata] %s', e) return None
def gen_blocks(block_count, state, miner_address): blocks = [] with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 prev_hash = bytes(sha2_256(b'test')) for i in range(0, block_count): block = Block.create(block_number=i, prevblock_headerhash=prev_hash, transactions=[], miner_address=miner_address) block.set_nonces(10, 0) blocks.append(block) metadata = BlockMetadata() metadata.set_block_difficulty(256) state.put_block_metadata(block.headerhash, metadata, None) state.put_block(block, None) prev_hash = bytes(block.headerhash) return blocks
def test_get_block_metadata(self): with set_qrl_dir('no_data'): with State() as state: self.assertIsNone(state.get_block_metadata(b'test1')) state.put_block_metadata(b'block_headerhash2', BlockMetadata.create(), None) tmp_json = state.get_block_metadata(b'block_headerhash2').to_json() expected_json = b'{\n "blockDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",\n ' \ b'"cumulativeDifficulty": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="\n}' self.assertEqual(tmp_json, expected_json)
def test_GetLastBlockHeader(self): self.block_header_params["blocknumber"] = 20 block_header = BlockHeader.create(**self.block_header_params) self.chain_manager.height = 200 self.qrlnode.get_blockheader_and_metadata = MagicMock( return_value=[block_header, BlockMetadata()]) req = qrlmining_pb2.GetLastBlockHeaderReq(height=20) answer = self.service.GetLastBlockHeader(request=req, context=None) self.assertEqual(180, answer.depth) self.assertEqual(200, answer.height)
def add_block_metadata(self, headerhash, block_timestamp, parent_headerhash, batch): parent_metadata = self.state.get_block_metadata(parent_headerhash) block_difficulty = (0,) * 32 # 32 bytes to represent 256 bit of 0 block_cumulative_difficulty = (0,) * 32 # 32 bytes to represent 256 bit of 0 if not parent_metadata: parent_metadata = BlockMetadata.create() else: parent_block = self.state.get_block(parent_headerhash) if parent_block: parent_block_difficulty = parent_metadata.block_difficulty parent_cumulative_difficulty = parent_metadata.cumulative_difficulty if not parent_metadata.is_orphan: measurement = self.state.get_measurement(block_timestamp, parent_headerhash) block_difficulty, _ = self._difficulty_tracker.get( measurement=measurement, parent_difficulty=parent_block_difficulty) block_cumulative_difficulty = StringToUInt256(str( int(UInt256ToString(block_difficulty)) + int(UInt256ToString(parent_cumulative_difficulty)))) block_metadata = self.state.get_block_metadata(headerhash) if not block_metadata: block_metadata = BlockMetadata.create() block_metadata.set_orphan(parent_metadata.is_orphan) block_metadata.set_block_difficulty(block_difficulty) block_metadata.set_cumulative_difficulty(block_cumulative_difficulty) parent_metadata.add_child_headerhash(headerhash) self.state.put_block_metadata(parent_headerhash, parent_metadata, batch) self.state.put_block_metadata(headerhash, block_metadata, batch)
def test_GetBlockMiningCompatible(self): block_header = BlockHeader.create(**self.block_header_params) self.qrlnode.get_blockheader_and_metadata = MagicMock( return_value=[block_header, BlockMetadata()]) req = qrlmining_pb2.GetBlockMiningCompatibleReq(height=10) answer = self.service.GetBlockMiningCompatible(request=req, context=None) self.assertEqual(10, answer.blockheader.block_number) self.assertEqual(1, answer.blockheader.reward_fee) # if QRLNode responds with None, None, the GRPC response should be blank too self.qrlnode.get_blockheader_and_metadata = MagicMock( return_value=[None, None]) answer = self.service.GetBlockMiningCompatible(request=req, context=None) self.assertEqual(0, answer.blockheader.block_number) self.assertEqual(0, answer.blockheader.reward_fee)
def put_block_metadata(self, headerhash: bytes, block_metadata: BlockMetadata, batch): self._db.put_raw(b'metadata_' + bin2hstr(headerhash).encode(), block_metadata.to_json(), batch)
def load(self, genesis_block): # load() has the following tasks: # Write Genesis Block into State immediately # Register block_number <-> blockhash mapping # Calculate difficulty Metadata for Genesis Block # Generate AddressStates from Genesis Block balances # Apply Genesis Block's transactions to the state # Detect if we are forked from genesis block and if so initiate recovery. height = self._state.get_mainchain_height() if height == -1: self._state.put_block(genesis_block, None) block_number_mapping = qrl_pb2.BlockNumberMapping( headerhash=genesis_block.headerhash, prev_headerhash=genesis_block.prev_headerhash) self._state.put_block_number_mapping(genesis_block.block_number, block_number_mapping, None) parent_difficulty = StringToUInt256( str(config.user.genesis_difficulty)) self.current_difficulty, _ = DifficultyTracker.get( measurement=config.dev.mining_setpoint_blocktime, parent_difficulty=parent_difficulty) block_metadata = BlockMetadata.create() block_metadata.set_block_difficulty(self.current_difficulty) block_metadata.set_cumulative_difficulty(self.current_difficulty) self._state.put_block_metadata(genesis_block.headerhash, block_metadata, None) addresses_state = dict() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address addresses_state[bytes_addr] = AddressState.get_default( bytes_addr) addresses_state[ bytes_addr]._data.balance = genesis_balance.balance for tx_idx in range(1, len(genesis_block.transactions)): tx = Transaction.from_pbdata( genesis_block.transactions[tx_idx]) for addr in tx.addrs_to: addresses_state[addr] = AddressState.get_default(addr) coinbase_tx = Transaction.from_pbdata( genesis_block.transactions[0]) if not isinstance(coinbase_tx, CoinBase): return False addresses_state[coinbase_tx.addr_to] = AddressState.get_default( coinbase_tx.addr_to) if not coinbase_tx.validate_extended(genesis_block.block_number): return False coinbase_tx.apply_state_changes(addresses_state) for tx_idx in range(1, len(genesis_block.transactions)): tx = Transaction.from_pbdata( genesis_block.transactions[tx_idx]) tx.apply_state_changes(addresses_state) self._state.put_addresses_state(addresses_state) self._state.update_tx_metadata(genesis_block, None) self._state.update_mainchain_height(0, None) else: self._last_block = self.get_block_by_number(height) self.current_difficulty = self._state.get_block_metadata( self._last_block.headerhash).block_difficulty fork_state = self._state.get_fork_state() if fork_state: block = self._state.get_block(fork_state.initiator_headerhash) self._fork_recovery(block, fork_state)
def put_block_metadata(self, headerhash: bytes, block_metadata: BlockMetadata, batch): self._db.put_raw(b'metadata_' + headerhash, block_metadata.serialize(), batch)
def gen_blocks(block_count, state, miner_address): blocks = [] block = None with mock.patch('qrl.core.misc.ntp.getTime') as time_mock: time_mock.return_value = 1615270948 addresses_state = dict() for i in range(0, block_count): if i == 0: block = GenesisBlock() for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address addresses_state[ bytes_addr] = OptimizedAddressState.get_default( bytes_addr) addresses_state[ bytes_addr]._data.balance = genesis_balance.balance else: block = Block.create(dev_config=config.dev, block_number=i, prev_headerhash=block.headerhash, prev_timestamp=block.timestamp, transactions=[], miner_address=miner_address, seed_height=None, seed_hash=None) addresses_set = ChainManager.set_affected_address(block) coin_base_tx = Transaction.from_pbdata(block.transactions[0]) coin_base_tx.set_affected_address(addresses_set) chain_manager = ChainManager(state) state_container = chain_manager.new_state_container( addresses_set, block.block_number, False, None) coin_base_tx.apply(state, state_container) for tx_idx in range(1, len(block.transactions)): tx = Transaction.from_pbdata(block.transactions[tx_idx]) if not chain_manager.update_state_container( tx, state_container): return False tx.apply(state, state_container) block.set_nonces(dev_config=config.dev, mining_nonce=10, extra_nonce=0) blocks.append(block) metadata = BlockMetadata() metadata.set_block_difficulty(StringToUInt256('256')) BlockMetadata.put_block_metadata(state, block.headerhash, metadata, None) Block.put_block(state, block, None) bm = qrl_pb2.BlockNumberMapping( headerhash=block.headerhash, prev_headerhash=block.prev_headerhash) Block.put_block_number_mapping(state, block.block_number, bm, None) state.update_mainchain_height(block.block_number, None) OptimizedAddressState.put_optimized_addresses_state( state, addresses_state) return blocks