def rollback_tx_metadata(state: State, block, batch): fee_reward = 0 for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) fee_reward += txn.fee TransactionMetadata.remove_tx_metadata(state, txn, batch) txn = Transaction.from_pbdata(block.transactions[0]) # Coinbase Transaction state._update_total_coin_supply(fee_reward - txn.amount, batch) LastTransactions._remove_last_tx(state, block, batch)
def validate(self, chain_manager, future_blocks: OrderedDict) -> bool: if chain_manager.get_block_is_duplicate(self): logger.warning('Duplicate Block #%s %s', self.block_number, bin2hstr(self.headerhash)) return False parent_block = chain_manager.get_block(self.prev_headerhash) # If parent block not found in state, then check if its in the future block list if not parent_block: try: parent_block = future_blocks[self.prev_headerhash] except KeyError: logger.warning('Parent block not found') logger.warning('Parent block headerhash %s', bin2hstr(self.prev_headerhash)) return False if not self._validate_parent_child_relation(parent_block): logger.warning('Failed to validate blocks parent child relation') return False if not chain_manager.validate_mining_nonce(self.blockheader): logger.warning('Failed PoW Validation') return False fee_reward = 0 for index in range(1, len(self.transactions)): fee_reward += self.transactions[index].fee if len(self.transactions) == 0: return False try: coinbase_txn = Transaction.from_pbdata(self.transactions[0]) coinbase_amount = coinbase_txn.amount if not coinbase_txn.validate_extended(self.block_number): return False except Exception as e: logger.warning('Exception %s', e) return False hashedtransactions = [] for tx in self.transactions: tx = Transaction.from_pbdata(tx) hashedtransactions.append(tx.txhash) if not self.blockheader.validate(fee_reward, coinbase_amount, merkle_tx_hash(hashedtransactions)): return False return True
def tx_push(ctx, txblob): """ Sends a signed transaction blob to a node """ tx = None try: txbin = parse_hexblob(txblob) pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(txbin) tx = Transaction.from_pbdata(pbdata) except Exception as e: click.echo("tx blob is not valid") quit(1) tmp_json = tx.to_json() # FIXME: binary fields are represented in base64. Improve output print(tmp_json) if len(tx.signature) == 0: click.echo('Signature missing') quit(1) stub = ctx.obj.get_stub_public_api() pushTransactionReq = qrl_pb2.PushTransactionReq( transaction_signed=tx.pbdata) pushTransactionResp = stub.PushTransaction(pushTransactionReq, timeout=CONNECTION_TIMEOUT) print(pushTransactionResp.error_code)
def PushTransaction(self, request: qrl_pb2.PushTransactionReq, context) -> qrl_pb2.PushTransactionResp: logger.debug("[PublicAPI] PushTransaction") answer = qrl_pb2.PushTransactionResp() try: tx = Transaction.from_pbdata(request.transaction_signed) tx.update_txhash() # FIXME: Full validation takes too much time. At least verify there is a signature # the validation happens later in the tx pool if len(tx.signature) > 1000: self.qrlnode.submit_send_tx(tx) answer.error_code = qrl_pb2.PushTransactionResp.SUBMITTED answer.tx_hash = tx.txhash else: answer.error_description = 'Signature too short' answer.error_code = qrl_pb2.PushTransactionResp.VALIDATION_FAILED except Exception as e: error_str = traceback.format_exception(None, e, e.__traceback__) answer.error_description = str(''.join(error_str)) answer.error_code = qrl_pb2.PushTransactionResp.ERROR return answer
def get_tx_metadata(state: State, txhash: bytes): try: tx_metadata = TransactionMetadata.deserialize(state._db.get_raw(txhash)) data, block_number = tx_metadata.transaction, tx_metadata.block_number return Transaction.from_pbdata(data), block_number except Exception: return None
def apply_state_changes(self, address_txn) -> bool: coinbase_tx = Transaction.from_pbdata(self.transactions[0]) if not coinbase_tx.validate_extended(self.block_number): logger.warning('Coinbase transaction failed') return False coinbase_tx.apply_state_changes(address_txn) len_transactions = len(self.transactions) for tx_idx in range(1, len_transactions): tx = Transaction.from_pbdata(self.transactions[tx_idx]) if isinstance(tx, CoinBase): logger.warning('Found another coinbase transaction') return False if not tx.validate(): return False addr_from_pk_state = address_txn[tx.addr_from] addr_from_pk = Transaction.get_slave(tx) if addr_from_pk: addr_from_pk_state = address_txn[addr_from_pk] if not tx.validate_extended(address_txn[tx.addr_from], addr_from_pk_state): return False expected_nonce = addr_from_pk_state.nonce + 1 if tx.nonce != expected_nonce: logger.warning('nonce incorrect, invalid tx') logger.warning('subtype: %s', tx.type) logger.warning('%s actual: %s expected: %s', tx.addr_from, tx.nonce, expected_nonce) return False if addr_from_pk_state.ots_key_reuse(tx.ots_key): logger.warning('pubkey reuse detected: invalid tx %s', bin2hstr(tx.txhash)) logger.warning('subtype: %s', tx.type) return False tx.apply_state_changes(address_txn) return True
def update_tx_metadata(state: State, block, batch) -> bool: fee_reward = 0 for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) fee_reward += txn.fee if not TransactionMetadata.put_tx_metadata(state, txn, block.block_number, block.timestamp, batch): return False txn = Transaction.from_pbdata(block.transactions[0]) # Coinbase Transaction state._update_total_coin_supply(txn.amount - fee_reward, batch) LastTransactions._update_last_tx(state, block, batch) return True
def test_update_mining_address(self): self.block.update_mining_address(dev_config=config.dev, mining_address=bob.address) coinbase_tx = Transaction.from_pbdata(self.block.transactions[0]) self.assertTrue(isinstance(coinbase_tx, CoinBase)) self.assertEqual(coinbase_tx.addr_to, bob.address) hashedtransactions = [] for tx in self.block.transactions: hashedtransactions.append(tx.transaction_hash) self.assertEqual(self.block.blockheader.tx_merkle_root, merkle_tx_hash(hashedtransactions))
def rollback_tx_metadata(self, block, batch): fee_reward = 0 for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) fee_reward += txn.fee self.remove_tx_metadata(txn, batch) # FIXME: Being updated without batch, need to fix, if isinstance(txn, TransferTokenTransaction): self.remove_transfer_token_metadata(txn) elif isinstance(txn, TokenTransaction): self.remove_token_metadata(txn) self._decrease_txn_count(self.get_txn_count(txn.addr_from), txn.addr_from) txn = Transaction.from_pbdata( block.transactions[0]) # Coinbase Transaction self._update_total_coin_supply(fee_reward - txn.amount) self._remove_last_tx(block, batch)
def update_mining_address(self, dev_config: DevConfig, mining_address: bytes): coinbase_tx = Transaction.from_pbdata(self.transactions[0]) coinbase_tx.update_mining_address(mining_address) hashedtransactions = [] for tx in self.transactions: hashedtransactions.append(tx.transaction_hash) self.blockheader.update_merkle_root(dev_config, merkle_tx_hash(hashedtransactions)) self._data.header.MergeFrom(self.blockheader.pbdata)
def update_tx_metadata(self, block, batch): fee_reward = 0 # TODO (cyyber): Move To State Cache, instead of writing directly for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) fee_reward += txn.fee self.put_tx_metadata(txn, block.block_number, block.timestamp, batch) # FIXME: Being updated without batch, need to fix, if isinstance(txn, TransferTokenTransaction): self.update_token_metadata(txn) elif isinstance(txn, TokenTransaction): self.create_token_metadata(txn) self._increase_txn_count(self.get_txn_count(txn.addr_from), txn.addr_from) txn = Transaction.from_pbdata( block.transactions[0]) # Coinbase Transaction self._update_total_coin_supply(txn.amount - fee_reward) self._update_last_tx(block, batch)
def prepare_address_list(block) -> set: addresses = set() for proto_tx in block.transactions: tx = Transaction.from_pbdata(proto_tx) tx.set_affected_address(addresses) for genesis_balance in GenesisBlock().genesis_balance: bytes_addr = genesis_balance.address if bytes_addr not in addresses: addresses.add(bytes_addr) return addresses
def _remove_last_tx(self, block, batch): if len(block.transactions) == 0: return try: last_txn = LastTransactions.deserialize( self._db.get_raw(b'last_txn')) except: # noqa return for protobuf_txn in block.transactions: txn = Transaction.from_pbdata(protobuf_txn) i = 0 while i < len(last_txn.tx_metadata): tx = Transaction.from_pbdata( last_txn.tx_metadata[i].transaction) if txn.txhash == tx.txhash: del last_txn.tx_metadata[i] break i += 1 self._db.put_raw(b'last_txn', last_txn.serialize(), batch)
def get_genesis_with_only_coin_base_txn(coin_base_reward_addr, dev_config): g = GenesisBlock() coin_base_tx = Transaction.from_pbdata(g.transactions[0]) coin_base_tx.update_mining_address(coin_base_reward_addr) # Remove all other transaction except CoinBase txn del g.transactions[:] g.pbdata.transactions.extend([coin_base_tx.pbdata]) g.blockheader.generate_headerhash(dev_config) return g
def _remove_block_from_mainchain(self, block: Block, latest_block_number: int, batch): addresses_set = self._state.prepare_address_list(block) addresses_state = self._state.get_state_mainchain(addresses_set) for tx_idx in range(len(block.transactions) - 1, -1, -1): tx = Transaction.from_pbdata(block.transactions[tx_idx]) tx.revert_state_changes(addresses_state, self) self.tx_pool.add_tx_from_block_to_pool(block, latest_block_number) self._state.update_mainchain_height(block.block_number - 1, batch) self._state.rollback_tx_metadata(block, batch) self._state.remove_blocknumber_mapping(block.block_number, batch) self._state.put_addresses_state(addresses_state, batch)
def get_last_txs(self): try: last_txn = LastTransactions.deserialize( self._db.get_raw(b'last_txn')) except: # noqa return [] txs = [] for tx_metadata in last_txn.tx_metadata: data = tx_metadata.transaction tx = Transaction.from_pbdata(data) txs.append(tx) return txs
def add_tx_from_block_to_pool(self, block: Block, current_block_number): """ Move all transactions from block to transaction pool. :param block: :return: """ for protobuf_tx in block.transactions[1:]: if not self.add_tx_to_pool(Transaction.from_pbdata(protobuf_tx), current_block_number): logger.warning( 'Failed to Add transaction into transaction pool') logger.warning('Block #%s %s', block.block_number, bin2hstr(block.headerhash)) return
def test_update_mining_address(self): alice_xmss = get_alice_xmss() bob_xmss = get_bob_xmss() block = Block.create(block_number=5, prev_headerhash=bytes(sha2_256(b'test')), prev_timestamp=10, transactions=[], miner_address=alice_xmss.address) block.update_mining_address(mining_address=bob_xmss.address) coinbase_tx = Transaction.from_pbdata(block.transactions[0]) self.assertTrue(isinstance(coinbase_tx, CoinBase)) self.assertEqual(coinbase_tx.addr_to, bob_xmss.address) hashedtransactions = [] for tx in block.transactions: hashedtransactions.append(tx.transaction_hash) self.assertEqual(block.blockheader.tx_merkle_root, merkle_tx_hash(hashedtransactions))
def get_last_txs(state: State): try: last_txn = LastTransactions.deserialize( state._db.get_raw(b'last_txn')) except KeyError: return [] except Exception as e: # noqa logger.warning("[get_last_txs] Exception during call %s", e) return [] txs = [] for tx_metadata in last_txn.tx_metadata: data = tx_metadata.transaction tx = Transaction.from_pbdata(data) txs.append(tx) return txs
def tx_inspect(ctx, txblob): """ Inspected a transaction blob """ tx = None try: txbin = parse_hexblob(txblob) pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(txbin) tx = Transaction.from_pbdata(pbdata) except Exception as e: click.echo("tx blob is not valid") quit(1) tmp_json = tx.to_json() # FIXME: binary fields are represented in base64. Improve output print(tmp_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 handle_transfer_token_transaction(source, message: qrllegacy_pb2.LegacyMessage): """ Transfer Token Transaction This function processes whenever a Transaction having subtype TRANSFERTOKEN is received. :return: """ P2PBaseObserver._validate_message(message, qrllegacy_pb2.LegacyMessage.TT) try: tx = Transaction.from_pbdata(message.ttData) except Exception as e: logger.error('Transfer Token Txn rejected - unable to decode serialised data - closing connection') logger.exception(e) source.loseConnection() return if source.factory.master_mr.isRequested(tx.get_message_hash(), source): source.factory.add_unprocessed_txn(tx, source.peer.ip)
def handle_slave(source, message: qrllegacy_pb2.LegacyMessage): """ Receives Slave Transaction :param source: :param message: :return: """ P2PBaseObserver._validate_message(message, qrllegacy_pb2.LegacyMessage.SL) try: tx = Transaction.from_pbdata(message.slData) except Exception as e: logger.error('slave_txn rejected - unable to decode serialised data - closing connection') logger.exception(e) source.loseConnection() return if source.factory.master_mr.isRequested(tx.get_message_hash(), source): source.factory.add_unprocessed_txn(tx, source.peer.ip)
def _update_last_tx(self, block, batch): if len(block.transactions) == 0: return last_txn = LastTransactions() try: last_txn = LastTransactions.deserialize( self._db.get_raw(b'last_txn')) except: # noqa pass for protobuf_txn in block.transactions[-20:]: txn = Transaction.from_pbdata(protobuf_txn) if isinstance(txn, CoinBase): continue last_txn.add(txn, block.block_number, block.timestamp) self._db.put_raw(b'last_txn', last_txn.serialize(), batch)
def handle_tx(source, message: qrllegacy_pb2.LegacyMessage): """ Transaction Executed whenever a new TX type message is received. :return: """ P2PBaseObserver._validate_message(message, qrllegacy_pb2.LegacyMessage.TX) try: tx = Transaction.from_pbdata(message.txData) except Exception as e: logger.error('Message Txn rejected - unable to decode serialised data - closing connection') logger.exception(e) source.loseConnection() return # NOTE: Connects to MR if source.factory.master_mr.isRequested(tx.get_message_hash(), source): source.factory.add_unprocessed_txn(tx, source.peer.ip)
def remove_tx_in_block_from_pool(self, block_obj: Block): for protobuf_tx in block_obj.transactions[ 1:]: # Ignore first transaction, as it is a coinbase txn tx = Transaction.from_pbdata(protobuf_tx) if tx.ots_key < config.dev.max_ots_tracking_index: idx = self.get_tx_index_from_pool(tx.txhash) if idx > -1: del self.transaction_pool[idx] else: i = 0 while i < len(self.transaction_pool): txn = self.transaction_pool[i][1].transaction if txn.PK == tx.PK: if txn.ots_key >= config.dev.max_ots_tracking_index: if txn.ots_key <= tx.ots_key: del self.transaction_pool[i] continue i += 1 heapq.heapify(self.transaction_pool)
def GetObject(self, request: qrl_pb2.GetObjectReq, context) -> qrl_pb2.GetObjectResp: logger.debug("[PublicAPI] GetObject") answer = qrl_pb2.GetObjectResp() answer.found = False # FIXME: We need a unified way to access and validate data. query = bytes(request.query) # query will be as a string, if Q is detected convert, etc. try: if AddressState.address_is_valid(query): if self.qrlnode.get_address_is_used(query): address_state = self.qrlnode.get_address_state(query) if address_state is not None: answer.found = True answer.address_state.CopyFrom(address_state.pbdata) return answer except ValueError: pass transaction_block_number = self.qrlnode.get_transaction(query) transaction = None blockheader = None if transaction_block_number: transaction, block_number = transaction_block_number answer.found = True block = self.qrlnode.get_block_from_index(block_number) blockheader = block.blockheader.pbdata timestamp = block.blockheader.timestamp else: transaction_timestamp = self.qrlnode.get_unconfirmed_transaction(query) if transaction_timestamp: transaction, timestamp = transaction_timestamp answer.found = True if transaction: txextended = qrl_pb2.TransactionExtended(header=blockheader, tx=transaction.pbdata, addr_from=transaction.addr_from, size=transaction.size, timestamp_seconds=timestamp) answer.transaction.CopyFrom(txextended) return answer # NOTE: This is temporary, indexes are accepted for blocks try: block = self.qrlnode.get_block_from_hash(query) if block is None: query_str = query.decode() query_index = int(query_str) block = self.qrlnode.get_block_from_index(query_index) if not block: return answer answer.found = True block_extended = qrl_pb2.BlockExtended() block_extended.header.CopyFrom(block.blockheader.pbdata) block_extended.size = block.size for transaction in block.transactions: tx = Transaction.from_pbdata(transaction) extended_tx = qrl_pb2.TransactionExtended(tx=transaction, addr_from=tx.addr_from, size=tx.size, timestamp_seconds=block.blockheader.timestamp) block_extended.extended_transactions.extend([extended_tx]) answer.block_extended.CopyFrom(block_extended) return answer except Exception: pass return answer
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 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
def validate(self, chain_manager, future_blocks: OrderedDict) -> bool: if chain_manager.get_block_is_duplicate(self): logger.warning('Duplicate Block #%s %s', self.block_number, bin2hstr(self.headerhash)) return False parent_block = chain_manager.get_block(self.prev_headerhash) dev_config = chain_manager.get_config_by_block_number(self.block_number) # If parent block not found in state, then check if its in the future block list if not parent_block: try: parent_block = future_blocks[self.prev_headerhash] except KeyError: logger.warning('Parent block not found') logger.warning('Parent block headerhash %s', bin2hstr(self.prev_headerhash)) return False if not self._validate_parent_child_relation(parent_block): logger.warning('Failed to validate blocks parent child relation') return False if not chain_manager.validate_mining_nonce(self.blockheader, dev_config): logger.warning('Failed PoW Validation') return False if len(self.transactions) == 0: return False try: state_container = chain_manager.new_state_container(set(), self.block_number, False, None) coinbase_txn = Transaction.from_pbdata(self.transactions[0]) coinbase_amount = coinbase_txn.amount if not coinbase_txn.validate_all(state_container): return False if self.block_number != 2078158: for proto_tx in self.transactions[1:]: if proto_tx.WhichOneof('transactionType') == 'coinbase': logger.warning("Multiple coinbase transaction found") return False except Exception as e: logger.warning('Exception %s', e) return False # Build transaction merkle tree, calculate fee reward, and then see if BlockHeader also agrees. hashedtransactions = [] for tx in self.transactions: tx = Transaction.from_pbdata(tx) hashedtransactions.append(tx.txhash) fee_reward = 0 for index in range(1, len(self.transactions)): fee_reward += self.transactions[index].fee qn = Qryptonight() seed_block = chain_manager.get_block_by_number(qn.get_seed_height(self.block_number)) self.blockheader._seed_height = seed_block.block_number self.blockheader._seed_hash = seed_block.headerhash if not self.blockheader.validate(fee_reward, coinbase_amount, merkle_tx_hash(hashedtransactions), dev_config): return False return True