コード例 #1
0
ファイル: ChainManager.py プロジェクト: scottdonaldau/QRL
    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
コード例 #2
0
    def prepare_next_unmined_block_template(self,
                                            mining_address,
                                            tx_pool,
                                            parent_block: Block,
                                            parent_difficulty,
                                            dev_config: DevConfig):
        miner = self.get_miner(parent_block.block_number + 1, dev_config)
        try:
            logger.debug('Miner-Try - prepare_next_unmined_block_template')
            with self.lock:
                logger.debug('Miner-Locked - prepare_next_unmined_block_template')

                logger.debug('Miner-TryCancel - prepare_next_unmined_block_template')
                miner.cancel()
                logger.debug('Miner-Cancel - prepare_next_unmined_block_template')

                self._mining_block = self.create_block(last_block=parent_block,
                                                       mining_nonce=0,
                                                       tx_pool=tx_pool,
                                                       miner_address=mining_address)

                parent_metadata = self._chain_manager.get_block_metadata(parent_block.headerhash)
                self._measurement = self._chain_manager.get_measurement(dev_config,
                                                                        self._mining_block.timestamp,
                                                                        self._mining_block.prev_headerhash,
                                                                        parent_metadata)

                self._current_difficulty, self._current_target = DifficultyTracker.get(
                    measurement=self._measurement,
                    parent_difficulty=parent_difficulty,
                    dev_config=dev_config)

        except Exception as e:
            logger.warning("Exception in start_mining")
            logger.exception(e)
コード例 #3
0
ファイル: ChainManager.py プロジェクト: som-dev/QRL
    def validate_mining_nonce(self, block, enable_logging=False):
        parent_metadata = self.state.get_block_metadata(block.prev_headerhash)
        parent_block = self.state.get_block(block.prev_headerhash)

        measurement = self.state.get_measurement(block.timestamp,
                                                 block.prev_headerhash,
                                                 parent_metadata)
        diff, target = DifficultyTracker.get(
            measurement=measurement,
            parent_difficulty=parent_metadata.block_difficulty)

        if enable_logging:
            logger.debug('-----------------START--------------------')
            logger.debug('Validate #%s', block.block_number)
            logger.debug('block.timestamp %s', block.timestamp)
            logger.debug('parent_block.timestamp %s', parent_block.timestamp)
            logger.debug('parent_block.difficulty %s',
                         UInt256ToString(parent_metadata.block_difficulty))
            logger.debug('diff : %s | target : %s', UInt256ToString(diff),
                         target)
            logger.debug('-------------------END--------------------')

        if not self.verify_input_cached(block.mining_blob, target):
            if enable_logging:
                logger.warning("PoW verification failed")
                qn = Qryptonight()
                tmp_hash = qn.hash(block.mining_blob)
                logger.warning("{}".format(tmp_hash))
                logger.debug('%s', block.to_json())
            return False

        return True
コード例 #4
0
    def prepare_next_unmined_block_template(self, tx_pool, parent_block: Block,
                                            parent_difficulty):
        mining_xmss = self.get_mining_xmss()
        if not mining_xmss:
            logger.warning('No Mining XMSS Found')
            return

        try:
            self.cancel()
            self._mining_block = self.create_block(
                last_block=parent_block,
                mining_nonce=0,
                tx_pool=tx_pool,
                signing_xmss=self._mining_xmss,
                master_address=self._master_address)

            parent_metadata = self.state.get_block_metadata(
                parent_block.headerhash)
            self._measurement = self.state.get_measurement(
                self._mining_block.timestamp,
                self._mining_block.prev_headerhash, parent_metadata)

            self._current_difficulty, self._current_target = DifficultyTracker.get(
                measurement=self._measurement,
                parent_difficulty=parent_difficulty)

        except Exception as e:
            logger.warning("Exception in start_mining")
            logger.exception(e)
コード例 #5
0
    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
コード例 #6
0
ファイル: ChainManager.py プロジェクト: amitkumarj441/QRL
    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
コード例 #7
0
ファイル: ChainManager.py プロジェクト: som-dev/QRL
    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)
コード例 #8
0
ファイル: Miner.py プロジェクト: amitkumarj441/QRL
    def prepare_next_unmined_block_template(self, tx_pool, parent_block: Block, parent_difficulty):
        try:
            self.cancel()
            self._mining_block = self.create_block(last_block=parent_block,
                                                   mining_nonce=0,
                                                   tx_pool=tx_pool,
                                                   miner_address=self._mining_credit_wallet)

            parent_metadata = self.state.get_block_metadata(parent_block.headerhash)
            self._measurement = self.state.get_measurement(self._mining_block.timestamp,
                                                           self._mining_block.prev_headerhash,
                                                           parent_metadata)

            self._current_difficulty, self._current_target = DifficultyTracker.get(
                measurement=self._measurement,
                parent_difficulty=parent_difficulty)

        except Exception as e:
            logger.warning("Exception in start_mining")
            logger.exception(e)
コード例 #9
0
ファイル: ChainManager.py プロジェクト: scottdonaldau/QRL
    def validate_mining_nonce(self,
                              blockheader: BlockHeader,
                              enable_logging=True):
        with self.lock:
            parent_metadata = self.get_block_metadata(
                blockheader.prev_headerhash)
            parent_block = self._state.get_block(blockheader.prev_headerhash)

            measurement = self.get_measurement(blockheader.timestamp,
                                               blockheader.prev_headerhash,
                                               parent_metadata)
            diff, target = DifficultyTracker.get(
                measurement=measurement,
                parent_difficulty=parent_metadata.block_difficulty)

            if enable_logging:
                logger.debug('-----------------START--------------------')
                logger.debug('Validate                #%s',
                             blockheader.block_number)
                logger.debug('block.timestamp         %s',
                             blockheader.timestamp)
                logger.debug('parent_block.timestamp  %s',
                             parent_block.timestamp)
                logger.debug('parent_block.difficulty %s',
                             UInt256ToString(parent_metadata.block_difficulty))
                logger.debug('diff                    %s',
                             UInt256ToString(diff))
                logger.debug('target                  %s', bin2hstr(target))
                logger.debug('-------------------END--------------------')

            if not PoWValidator().verify_input(blockheader.mining_blob,
                                               target):
                if enable_logging:
                    logger.warning("PoW verification failed")
                    qn = Qryptonight()
                    tmp_hash = qn.hash(blockheader.mining_blob)
                    logger.warning("{}".format(bin2hstr(tmp_hash)))
                    logger.debug('%s', blockheader.to_json())
                return False

            return True
コード例 #10
0
ファイル: ChainManager.py プロジェクト: scottdonaldau/QRL
    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)
コード例 #11
0
    def test_verify(self):

        class CustomQMiner(Qryptominer):
            def __init__(self):
                Qryptominer.__init__(self)
                self._solution_lock = threading.Lock()
                self.nonce = None
                self.solution_blob = None

            def start(self, input, nonceOffset, target, thread_count):
                self.cancel()
                try:
                    self._solution_lock.release()
                except RuntimeError:
                    pass
                self._solution_lock.acquire(blocking=False)
                super().start(input, nonceOffset, target, thread_count)

            def wait_for_solution(self):
                self._solution_lock.acquire(blocking=True)
                self._solution_lock.release()

            def handleEvent(self, event):
                if event.type == SOLUTION:
                    self.nonce = event.nonce
                    self.solution_blob = self.solutionInput()
                    self._solution_lock.release()

        block_timestamp = 1515443508
        parent_block_timestamp = 1515443508

        # This could be the average of last N blocks
        measurement = block_timestamp - parent_block_timestamp

        parent_difficulty = (0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 4)

        new_diff, new_target = DifficultyTracker.get(
            measurement,
            parent_difficulty=parent_difficulty)

        self.assertEqual(new_diff, (0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 5))

        self.assertEqual(new_target, (
            51, 51, 51, 51, 51, 51, 51, 51,
            51, 51, 51, 51, 51, 51, 51, 51,
            51, 51, 51, 51, 51, 51, 51, 51,
            51, 51, 51, 51, 51, 51, 51, 51))

        block_json = read_data_file('core/example_block_mining.json')

        block = Block.from_json(block_json)

        expected_blob = (0, 231, 90, 101, 142, 20, 245, 183, 96, 5, 216, 159, 111, 239, 93, 217, 138, 10, 227, 159, 198,
                         207, 109, 238, 83, 220, 167, 148, 247, 200, 197, 41, 37, 36, 150, 12, 116, 85, 254, 0, 0, 0, 0,
                         0, 0, 0, 0, 0, 0, 0, 0, 181, 198, 40, 62, 106, 139, 108, 83, 216, 206, 161, 148, 50, 65, 212,
                         137, 94, 102, 124, 45, 51, 57, 43, 19, 51)
        self.assertEqual(expected_blob, tuple(block.mining_blob))

        custom_qminer = CustomQMiner()
        custom_qminer.start(input=block.mining_blob,
                            nonceOffset=block.mining_nonce_offset,
                            target=new_target,
                            thread_count=2)
        custom_qminer.wait_for_solution()

        expected_mined_blob = bytearray(expected_blob)
        tmp_offset = config.dev.mining_nonce_offset
        expected_mined_blob[tmp_offset:tmp_offset + 4] = custom_qminer.nonce.to_bytes(4,
                                                                                      byteorder='big',
                                                                                      signed=False)

        print(custom_qminer.nonce)
        self.assertEqual(tuple(expected_mined_blob), custom_qminer.solution_blob)
        self.assertTrue(PoWHelper().verifyInput(custom_qminer.solution_blob, new_target))
コード例 #12
0
    def test_verify(self):
        class CustomQMiner(Qryptominer):
            def __init__(self):
                Qryptominer.__init__(self)
                self._solution_lock = threading.Lock()
                self.nonce = None
                self.solution_blob = None

            def start(self, input, nonceOffset, target, thread_count):
                self.cancel()
                try:
                    self._solution_lock.release()
                except RuntimeError:
                    pass
                self._solution_lock.acquire(blocking=False)
                super().start(input, nonceOffset, target, thread_count)

            def wait_for_solution(self):
                self._solution_lock.acquire(blocking=True)
                self._solution_lock.release()

            def solutionEvent(self, nonce):
                print('Solution Found %s', nonce)
                self.nonce = nonce
                self.solution_blob = self.solutionInput()
                self._solution_lock.release()

        block_timestamp = 1515443508
        parent_block_timestamp = 1515443508

        # This could be the average of last N blocks
        measurement = block_timestamp - parent_block_timestamp

        parent_difficulty = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4)

        new_diff, new_target = DifficultyTracker.get(
            measurement, parent_difficulty=parent_difficulty)

        self.assertEqual(new_diff,
                         (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5))

        self.assertEqual(
            new_target,
            (51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
             51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51))

        block_json = read_data_file('core/example_block_mining.json')

        block = Block.from_json(block_json)

        expected_blob = tuple([
            241, 93, 178, 239, 171, 183, 27, 87, 2, 191, 178, 157, 32, 74, 254,
            207, 242, 82, 128, 197, 58, 86, 24, 90, 106, 33, 58, 82, 160, 251,
            118, 174, 45, 182, 72, 157, 142, 141, 219, 0, 0, 0, 15, 61, 11, 20,
            166, 132, 14, 29, 248, 65, 55, 56, 226, 12, 57, 60, 37, 64, 123,
            44, 48, 172, 218, 221, 26, 8, 143, 110, 38, 215, 83, 248, 227, 87,
            148, 88, 237, 48, 203, 111, 245, 31, 125, 45, 14, 111, 109, 0, 87,
            13, 154, 252, 49, 160
        ])

        self.assertEqual(expected_blob, tuple(block.mining_blob))

        custom_qminer = CustomQMiner()
        custom_qminer.start(input=block.mining_blob,
                            nonceOffset=block.mining_nonce_offset,
                            target=new_target,
                            thread_count=2)
        custom_qminer.wait_for_solution()

        expected_mined_blob = bytearray(expected_blob)
        tmp_offset = config.dev.mining_nonce_offset
        expected_mined_blob[tmp_offset:tmp_offset +
                            4] = custom_qminer.nonce.to_bytes(4,
                                                              byteorder='big',
                                                              signed=False)

        print(custom_qminer.nonce)
        self.assertEqual(tuple(expected_mined_blob),
                         custom_qminer.solution_blob)
        self.assertTrue(PoWHelper().verifyInput(custom_qminer.solution_blob,
                                                new_target))
コード例 #13
0
ファイル: ChainManager.py プロジェクト: fanff/QRL
class ChainManager:
    def __init__(self, state):
        self.state = state
        self.tx_pool = TransactionPool()  # TODO: Move to some pool manager
        self.last_block = Block.from_json(GenesisBlock().to_json())
        self.current_difficulty = StringToUInt256(str(config.dev.genesis_difficulty))
        self._difficulty_tracker = DifficultyTracker()

        self.trigger_miner = False

    @property
    def height(self):
        return self.last_block.block_number

    def get_last_block(self) -> Block:
        return self.last_block

    def get_cumulative_difficulty(self):
        last_block_metadata = self.state.get_block_metadata(self.last_block.headerhash)
        return last_block_metadata.cumulative_difficulty

    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 validate_mining_nonce(self, block, enable_logging=False):
        parent_metadata = self.state.get_block_metadata(block.prev_headerhash)
        parent_block = self.state.get_block(block.prev_headerhash)
        input_bytes = StringToUInt256(str(block.mining_nonce))[-4:] + tuple(block.mining_hash)

        measurement = self.state.get_measurement(block.timestamp, block.prev_headerhash)
        diff, target = self._difficulty_tracker.get(
            measurement=measurement,
            parent_difficulty=parent_metadata.block_difficulty)

        if enable_logging:
            logger.debug('-----------------START--------------------')
            logger.debug('Validate #%s', block.block_number)
            logger.debug('block.timestamp %s', block.timestamp)
            logger.debug('parent_block.timestamp %s', parent_block.timestamp)
            logger.debug('parent_block.difficulty %s', UInt256ToString(parent_metadata.block_difficulty))
            logger.debug('input_bytes %s', UInt256ToString(input_bytes))
            logger.debug('diff : %s | target : %s', UInt256ToString(diff), target)
            logger.debug('-------------------END--------------------')

        if not PoWHelper.verifyInput(input_bytes, target):
            if enable_logging:
                logger.warning("PoW verification failed")
                qn = Qryptonight()
                tmp_hash = qn.hash(input_bytes)
                logger.warning("{}".format(tmp_hash))
                logger.debug('%s', block.to_json())
            return False

        return True

    def validate_block(self, block, address_txn) -> bool:
        len_transactions = len(block.transactions)

        if len_transactions < 1:
            return False

        coinbase_tx = Transaction.from_pbdata(block.transactions[0])
        coinbase_tx.validate()

        if not self.validate_mining_nonce(block):
            return False

        if coinbase_tx.subtype != qrl_pb2.Transaction.COINBASE:
            return False

        if not coinbase_tx.validate():
            return False

        coinbase_tx.apply_on_state(address_txn)

        addr_from_pk_state = address_txn[coinbase_tx.txto]
        addr_from_pk = Transaction.get_slave(coinbase_tx)
        if addr_from_pk:
            addr_from_pk_state = address_txn[addr_from_pk]

        if not coinbase_tx.validate_extended(address_txn[coinbase_tx.txto],
                                             addr_from_pk_state,
                                             []):
            return False

        # TODO: check block reward must be equal to coinbase amount

        for tx_idx in range(1, len_transactions):
            tx = Transaction.from_pbdata(block.transactions[tx_idx])

            if tx.subtype == qrl_pb2.Transaction.COINBASE:
                return False

            if not tx.validate():  # TODO: Move this validation, before adding txn to pool
                return False

            addr_from_pk_state = address_txn[tx.txfrom]
            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.txfrom], addr_from_pk_state, []):
                return False

            expected_nonce = address_txn[tx.txfrom].nonce + 1

            if tx.nonce != expected_nonce:
                logger.warning('nonce incorrect, invalid tx')
                logger.warning('subtype: %s', tx.subtype)
                logger.warning('%s actual: %s expected: %s', tx.txfrom, tx.nonce, expected_nonce)
                return False

            if tx.ots_key_reuse(address_txn[tx.txfrom], tx.ots_key):
                logger.warning('pubkey reuse detected: invalid tx %s', tx.txhash)
                logger.warning('subtype: %s', tx.subtype)
                return False

            tx.apply_on_state(address_txn)

        return True

    def _pre_check(self, block, ignore_duplicate):
        if block.block_number < 1:
            return False

        if not block.validate():
            return False

        if (not ignore_duplicate) and self.state.get_block(block.headerhash):  # Duplicate block check
            logger.info('Duplicate block %s %s', block.block_number, bin2hstr(block.headerhash))
            return False

        return True

    def _try_orphan_add_block(self, block, batch):
        prev_block_metadata = self.state.get_block_metadata(block.prev_headerhash)

        self.trigger_miner = False

        if prev_block_metadata is None or prev_block_metadata.is_orphan:
            self.state.put_block(block, batch)
            self.add_block_metadata(block.headerhash, block.timestamp, block.prev_headerhash, batch)
            return True

        return False

    def _try_branch_add_block(self, block, batch=None) -> bool:
        parent_block = self.state.get_block(block.prev_headerhash)
        if not block.validate_parent_child_relation(parent_block):
            logger.warning('Failed to validate blocks parent child relation')
            return False

        address_set = self.state.prepare_address_list(block)  # Prepare list for current block
        if self.last_block.headerhash == block.prev_headerhash:
            address_txn = self.state.get_state_mainchain(address_set)
        else:
            address_txn = self.state.get_state(block.prev_headerhash, address_set)

        if self.validate_block(block, address_txn):
            self.state.put_block(block, None)
            self.add_block_metadata(block.headerhash, block.timestamp, block.prev_headerhash, None)

            last_block_metadata = self.state.get_block_metadata(self.last_block.headerhash)
            new_block_metadata = self.state.get_block_metadata(block.headerhash)
            last_block_difficulty = int(UInt256ToString(last_block_metadata.cumulative_difficulty))
            new_block_difficulty = int(UInt256ToString(new_block_metadata.cumulative_difficulty))

            self.trigger_miner = False
            if new_block_difficulty > last_block_difficulty:
                if self.last_block.headerhash != block.prev_headerhash:
                    self.rollback(block)
                    return True

                self.state.update_mainchain_state(address_txn, block.block_number, block.headerhash)
                self.last_block = block
                self._update_mainchain(block, batch)
                self.tx_pool.remove_tx_in_block_from_pool(block)
                self.state.update_mainchain_height(block.block_number, batch)
                self.state.update_tx_metadata(block, batch)

                self.trigger_miner = True

            return True

        return False

    def rollback(self, block):
        hash_path = []
        while True:
            if self.state.state_objects.contains(block.headerhash):
                break
            hash_path.append(block.headerhash)
            new_block = self.state.get_block(block.prev_headerhash)
            if not new_block:
                logger.warning('No block found %s', block.prev_headerhash)
                break
            block = new_block
            if block.block_number == 0:
                del hash_path[-1]  # Skip replaying Genesis Block
                break

        self.state.state_objects.destroy_current_state(None)
        block = self.state.get_block(hash_path[-1])
        self.state.state_objects.destroy_fork_states(block.block_number, block.headerhash)

        for header_hash in hash_path[-1::-1]:
            block = self.state.get_block(header_hash)
            address_set = self.state.prepare_address_list(block)  # Prepare list for current block
            address_txn = self.state.get_state_mainchain(address_set)

            self.state.update_mainchain_state(address_txn, block.block_number, block.headerhash)
            self.last_block = block
            self._update_mainchain(block, None)
            self.tx_pool.remove_tx_in_block_from_pool(block)
            self.state.update_mainchain_height(block.block_number, None)
            self.state.update_tx_metadata(block, None)

        self.trigger_miner = True

    def _add_block(self, block, ignore_duplicate=False, batch=None):
        block_size_limit = self.state.get_block_size_limit(block)
        if block_size_limit and block.size > block_size_limit:
            logger.info('Block Size greater than threshold limit %s > %s', block.size, block_size_limit)
            return False

        if not self._pre_check(block, ignore_duplicate):
            logger.debug('Failed pre_check')
            return False

        if self._try_orphan_add_block(block, batch):
            return True

        if self._try_branch_add_block(block, batch):
            return True

        return False

    def add_block(self, block: Block) -> bool:
        if block.block_number < self.height - config.dev.reorg_limit:
            logger.debug('Skipping block #%s as beyond re-org limit', block.block_number)
            return False

        batch = self.state.get_batch()
        if self._add_block(block, batch=batch):
            self.state.write_batch(batch)
            self.update_child_metadata(block.headerhash)
            return True

        return False

    def update_child_metadata(self, headerhash):
        block_metadata = self.state.get_block_metadata(headerhash)

        childs = list(block_metadata.child_headerhashes)

        while childs:
            child_headerhash = childs.pop(0)
            block = self.state.get_block(child_headerhash)
            if not block:
                continue
            if not self._add_block(block, True):
                self._prune([block.headerhash], None)
                continue
            block_metadata = self.state.get_block_metadata(child_headerhash)
            childs += block_metadata.child_headerhashes

    def _prune(self, childs, batch):
        while childs:
            child_headerhash = childs.pop(0)

            block_metadata = self.state.get_block_metadata(child_headerhash)
            childs += block_metadata.child_headerhashes

            self.state.delete(bin2hstr(child_headerhash).encode(), batch)
            self.state.delete(b'metadata_' + bin2hstr(child_headerhash).encode(), batch)

    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 _update_mainchain(self, block, batch):
        measurement = self.state.get_measurement(block.timestamp, block.prev_headerhash)

        self.current_difficulty, _ = self._difficulty_tracker.get(
            measurement=measurement,
            parent_difficulty=self.current_difficulty)

        block_number_mapping = None
        while block_number_mapping is None or block.headerhash != block_number_mapping.headerhash:
            block_number_mapping = qrl_pb2.BlockNumberMapping(headerhash=block.headerhash,
                                                              prev_headerhash=block.prev_headerhash)
            self.state.put_block_number_mapping(block.block_number, block_number_mapping, batch)
            block = self.state.get_block(block.prev_headerhash)
            block_number_mapping = self.state.get_block_number_mapping(block.block_number)

    def get_block_by_number(self, block_number) -> Optional[Block]:
        return self.state.get_block_by_number(block_number)

    def get_state(self, headerhash):
        return self.state.get_state(headerhash, set())

    def get_address(self, address):
        return self.state.get_address(address)

    def get_transaction(self, transaction_hash) -> list:
        for tx in self.tx_pool.transaction_pool:
            if tx.txhash == transaction_hash:
                return [tx, None]

        return self.state.get_tx_metadata(transaction_hash)

    def get_headerhashes(self, start_blocknumber):
        start_blocknumber = max(0, start_blocknumber)
        end_blocknumber = min(self.last_block.block_number,
                              start_blocknumber + config.dev.reorg_limit)

        node_header_hash = qrl_pb2.NodeHeaderHash()
        node_header_hash.block_number = start_blocknumber

        for i in range(start_blocknumber, end_blocknumber + 1):
            block = self.state.get_block_by_number(i)
            node_header_hash.headerhashes.append(block.headerhash)

        return node_header_hash

    def add_ephemeral_message(self, encrypted_ephemeral: EncryptedEphemeralMessage):
        self.state.update_ephemeral(encrypted_ephemeral)
コード例 #14
0
ファイル: Miner.py プロジェクト: fanff/QRL
class Miner(Qryptominer):
    def __init__(self, pre_block_logic, slaves: list, state: State, add_unprocessed_txn_fn):
        super().__init__()
        self.pre_block_logic = pre_block_logic  # FIXME: Circular dependency with node.py
        self._mining_block = None
        self._slaves = slaves
        self._mining_xmss = None
        self._reward_address = None
        self.state = state
        self._difficulty_tracker = DifficultyTracker()
        self._add_unprocessed_txn_fn = add_unprocessed_txn_fn

    @staticmethod
    def _get_mining_data(block):
        input_bytes = [0x00, 0x00, 0x00, 0x00] + list(block.mining_hash)
        nonce_offset = 0
        return input_bytes, nonce_offset

    @staticmethod
    def calc_hash(input_bytes):
        qn = Qryptonight()
        return qn.hash(input_bytes)

    def set_unused_ots_key(self, xmss, addr_state, start=0):
        for i in range(start, 2 ** xmss.height):
            if not Transaction.ots_key_reuse(addr_state, i):
                xmss.set_index(i)
                return True
        return False

    def valid_mining_permission(self):
        if self._master_address == self._mining_xmss.get_address():
            return True
        addr_state = self.state.get_address(self._master_address)
        access_type = addr_state.get_slave_permission(self._mining_xmss.pk())
        if access_type == -1:
            logger.warning('Slave is not authorized yet for mining')
            logger.warning('Added Slave Txn')
            slave_tx = Transaction.from_json(self._slaves[2])
            self._add_unprocessed_txn_fn(slave_tx, None)
            return None
        return True

    def get_mining_xmss(self):
        if self._mining_xmss:
            addr_state = self.state.get_address(self._mining_xmss.get_address())
            if self.set_unused_ots_key(self._mining_xmss, addr_state, self._mining_xmss.get_index()):
                if self.valid_mining_permission():
                    return self._mining_xmss
            else:
                self._mining_xmss = None
            return None

        if not self._mining_xmss:
            self._master_address = self._slaves[0].encode()
            unused_ots_found = False
            for slave_seed in self._slaves[1]:
                xmss = Wallet.get_new_address(seed=slave_seed).xmss
                addr_state = self.state.get_address(xmss.get_address())
                if self.set_unused_ots_key(xmss, addr_state):  # Unused ots_key_found
                    self._mining_xmss = xmss
                    unused_ots_found = True
                    break

            if not unused_ots_found:  # Unused ots_key_found
                logger.warning('No OTS-KEY left for mining')
                return None

        if self._master_address == self._mining_xmss.get_address():
            return self._mining_xmss

        if not self.valid_mining_permission():
            return None

        return self._mining_xmss

    def start_mining(self,
                     tx_pool,
                     parent_block,
                     parent_difficulty,
                     thread_count=config.user.mining_thread_count):

        mining_xmss = self.get_mining_xmss()
        if not mining_xmss:
            logger.warning('No Mining XMSS Found')
            return

        try:
            self.cancel()
            self._mining_block = self.create_block(last_block=parent_block,
                                                   mining_nonce=0,
                                                   tx_pool=tx_pool,
                                                   signing_xmss=self._mining_xmss,
                                                   master_address=self._master_address)

            measurement = self.state.get_measurement(self._mining_block.timestamp, self._mining_block.prev_headerhash)

            current_difficulty, current_target = self._difficulty_tracker.get(
                measurement=measurement,
                parent_difficulty=parent_difficulty)

            input_bytes, nonce_offset = self._get_mining_data(self._mining_block)
            logger.debug('!!! Mine #{} | {} ({}) | {} -> {} | {}'.format(
                self._mining_block.block_number,
                measurement, self._mining_block.timestamp - parent_block.timestamp,
                UInt256ToString(parent_difficulty), UInt256ToString(current_difficulty),
                current_target
            ))
            logger.debug('!!! {}'.format(current_target))
            self.start(input=input_bytes,
                       nonceOffset=nonce_offset,
                       target=current_target,
                       thread_count=thread_count)
        except Exception as e:
            logger.warning("Exception in start_mining")
            logger.exception(e)

    def solutionEvent(self, nonce):
        # NOTE: This function usually runs in the context of a C++ thread
        try:
            logger.debug('Solution Found %s', nonce)
            self._mining_block.set_mining_nonce(nonce)
            logger.info('Block #%s nonce: %s', self._mining_block.block_number, StringToUInt256(str(nonce))[-4:])
            logger.info('Hash Rate: %s H/s', self.hashRate())
            cloned_block = copy.deepcopy(self._mining_block)
            self.pre_block_logic(cloned_block)
        except Exception as e:
            logger.warning("Exception in solutionEvent")
            logger.exception(e)

    def create_block(self, last_block, mining_nonce, tx_pool, signing_xmss, master_address) -> Optional[Block]:
        # TODO: Persistence will move to rocksdb
        # FIXME: Difference between this and create block?????????????

        # FIXME: Break encapsulation
        dummy_block = Block.create(mining_nonce=mining_nonce,
                                   block_number=last_block.block_number + 1,
                                   prevblock_headerhash=last_block.headerhash,
                                   transactions=[],
                                   signing_xmss=signing_xmss,
                                   master_address=master_address,
                                   nonce=0)
        dummy_block.set_mining_nonce(mining_nonce)
        signing_xmss.set_index(signing_xmss.get_index() - 1)

        t_pool2 = copy.deepcopy(tx_pool.transaction_pool)
        del tx_pool.transaction_pool[:]
        ######

        # recreate the transaction pool as in the tx_hash_list, ordered by txhash..
        total_txn = len(t_pool2)
        txnum = 0
        addresses_set = set()
        while txnum < total_txn:
            tx = t_pool2[txnum]
            tx.set_effected_address(addresses_set)
            txnum += 1

        addresses_state = dict()
        for address in addresses_set:
            addresses_state[address] = self.state.get_address(address)

        block_size = dummy_block.size
        block_size_limit = self.state.get_block_size_limit(last_block)
        txnum = 0
        while txnum < total_txn:
            tx = t_pool2[txnum]
            # Skip Transactions for later, which doesn't fit into block
            if block_size + tx.size + config.dev.tx_extra_overhead > block_size_limit:
                txnum += 1
                continue

            addr_from_pk_state = addresses_state[tx.txfrom]
            addr_from_pk = Transaction.get_slave(tx)
            if addr_from_pk:
                addr_from_pk_state = addresses_state[addr_from_pk]

            if tx.ots_key_reuse(addr_from_pk_state, tx.ots_key):
                del t_pool2[txnum]
                total_txn -= 1
                continue

            if tx.subtype == qrl_pb2.Transaction.TRANSFER:
                if addresses_state[tx.txfrom].balance < tx.amount + tx.fee:
                    logger.warning('%s %s exceeds balance, invalid tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Transfer Amount %s', addresses_state[tx.txfrom].balance,
                                   tx.amount)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

            if tx.subtype == qrl_pb2.Transaction.MESSAGE:
                if addresses_state[tx.txfrom].balance < tx.fee:
                    logger.warning('%s %s exceeds balance, invalid message tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Free %s', addresses_state[tx.txfrom].balance, tx.fee)
                    total_txn -= 1
                    continue

            if tx.subtype == qrl_pb2.Transaction.TOKEN:
                if addresses_state[tx.txfrom].balance < tx.fee:
                    logger.warning('%s %s exceeds balance, invalid tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Fee %s',
                                   addresses_state[tx.txfrom].balance,
                                   tx.fee)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

            if tx.subtype == qrl_pb2.Transaction.TRANSFERTOKEN:
                if addresses_state[tx.txfrom].balance < tx.fee:
                    logger.warning('%s %s exceeds balance, invalid tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Transfer Amount %s',
                                   addresses_state[tx.txfrom].balance,
                                   tx.fee)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

                if bin2hstr(tx.token_txhash).encode() not in addresses_state[tx.txfrom].tokens:
                    logger.warning('%s doesnt own any token with token_txnhash %s', tx.txfrom,
                                   bin2hstr(tx.token_txhash).encode())
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

                if addresses_state[tx.txfrom].tokens[bin2hstr(tx.token_txhash).encode()] < tx.amount:
                    logger.warning('Token Transfer amount exceeds available token')
                    logger.warning('Token Txhash %s', bin2hstr(tx.token_txhash).encode())
                    logger.warning('Available Token Amount %s',
                                   addresses_state[tx.txfrom].tokens[bin2hstr(tx.token_txhash).encode()])
                    logger.warning('Transaction Amount %s', tx.amount)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

            if tx.subtype == qrl_pb2.Transaction.LATTICE:
                if addresses_state[tx.txfrom].balance < tx.fee:
                    logger.warning('Lattice TXN %s %s exceeds balance, invalid tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Transfer Amount %s',
                                   addresses_state[tx.txfrom].balance,
                                   tx.fee)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

            if tx.subtype == qrl_pb2.Transaction.SLAVE:
                if addresses_state[tx.txfrom].balance < tx.fee:
                    logger.warning('Slave TXN %s %s exceeds balance, invalid tx', tx, tx.txfrom)
                    logger.warning('subtype: %s', tx.subtype)
                    logger.warning('Buffer State Balance: %s  Transfer Amount %s',
                                   addresses_state[tx.txfrom].balance,
                                   tx.fee)
                    del t_pool2[txnum]
                    total_txn -= 1
                    continue

            tx.apply_on_state(addresses_state)

            tx_pool.add_tx_to_pool(tx)
            tx._data.nonce = addresses_state[tx.txfrom].nonce
            txnum += 1
            block_size += tx.size + config.dev.tx_extra_overhead

        coinbase_nonce = self.state.get_address(signing_xmss.get_address()).nonce
        if signing_xmss.get_address() in addresses_state:
            coinbase_nonce = addresses_state[signing_xmss.get_address()].nonce + 1

        block = Block.create(mining_nonce=mining_nonce,
                             block_number=last_block.block_number + 1,
                             prevblock_headerhash=last_block.headerhash,
                             transactions=t_pool2,
                             signing_xmss=signing_xmss,
                             master_address=master_address,
                             nonce=coinbase_nonce)

        return block