示例#1
0
def process_incoming_msg(msg: bytes, in_address: Address,
                         receive_queue: Queue):
    """ Process messages received from other nodes

    Args:
        msg: Received message
        in_address: Address of the sender
        receive_queue: Queue for communication with the blockchain
    """
    try:
        msg_type, msg_data = unpack_msg(msg)
    except ValueError as err:
        print_debug_info(f'Received invalid message\n {err}')
        return

    PEERS.peer_seen(in_address)

    print_debug_info('received: ' + msg_type)
    if msg_type.startswith('N_'):
        # networking messages
        if msg_type == 'N_new_peer':
            PEERS.peer_inferred(msg_data)
        elif msg_type == 'N_get_peers':
            get_peers(in_address)
        elif msg_type == 'N_ping':
            send_msg('N_pong', '', in_address)
        elif msg_type == 'N_pong':
            pass  # Only needed for peer update. Ignore!
    else:
        # blockchain messages

        receive_queue.put((msg_type, msg_data, in_address))
示例#2
0
def test_debug_info(capsys):
    """ Test printing of debug info
    """
    utils.set_debug()
    utils.print_debug_info('TEST')

    captured = capsys.readouterr()
    assert captured.out == '### DEBUG ### TEST\n'
示例#3
0
 def load_chain(self):
     if os.path.exists('ddos_bc_file.txt') and \
             os.stat('ddos_bc_file.txt').st_size != 0 and \
             Path('ddos_bc_file.txt').is_file():
         print_debug_info('Load existing blockchain from file')
         with open('ddos_bc_file.txt', 'r') as bc_file:
             self.chain = serializer.deserialize(bc_file.read())
     else:
         self.chain[DDosHeader(0, 0, 768894480, 0, 0)] = []
示例#4
0
    def new_block(self, block: Block):
        # Main chain
        if self.validate_block(block, self.latest_block()):
            self.process_block(block)
            self.chain[block.header] = block.transactions
            self.send_queue.put(('new_block', block, 'broadcast'))
            if self.gui_ready:
                self.gui_queue.put(('new_block', block, 'local'))
        else:
            print_debug_info('Block not for main chain')

        self.check_new_chain(block)
示例#5
0
    def check_new_chain(self, block):
        if block.header in self.new_chain:
            if block.header.root_hash ==\
                    self.create_merkle_root(block.transactions):
                # Validate transactions<->header
                self.new_chain[block.header] = block.transactions

                for t in block.transactions:
                    try:
                        # Remove processed transactions
                        self.intermediate_transactions.remove(t)
                    except ValueError:
                        pass

                # Check if new chain is finished
                if not any(t is None for t in self.new_chain.values()):
                    # Validate transactions
                    old_data = next(iter(self.new_chain.items()))
                    for h, t in self.new_chain.items():
                        if h == old_data[0]:
                            continue
                        if self.validate_block(Block(h, t),
                                               Block(old_data[0], old_data[1]),
                                               True):
                            old_data = (h, t)
                        else:
                            print_debug_info(
                                'Invalid transaction in new chain')
                            self.new_chain.clear()
                            self.intermediate_transactions.clear()
                            return

                    self.send_queue.put(
                        ('new_header', self.nc_latest_header(), 'broadcast'))
                    self.chain = OrderedDict(self.new_chain)
                    self.new_chain.clear()
                    # Remove mining transactions
                    not_processed_transactions = [
                        t for t in self.intermediate_transactions
                        if t.sender != '0'
                    ]
                    self.transaction_pool = list(not_processed_transactions)
                    self.intermediate_transactions.clear()

                    # DNS specific
                    try:
                        self._sync_auctions()  # type: ignore
                    except AttributeError:
                        pass

            else:
                print_debug_info('Block not for new chain')
示例#6
0
    def load_chain(self):
        """ Loads Blockchain from the hard drive.
        """
        if os.path.exists('bc_file.txt') and \
                os.stat('bc_file.txt').st_size != 0 and \
                Path('bc_file.txt').is_file():
            print_debug_info('Load existing blockchain from file')
            with open('bc_file.txt', 'r') as bc_file:
                self.chain = serializer.deserialize(bc_file.read())
        else:
            # If file doesn't exist / is empty:
            # Create genesis block

            self.chain[Header(0, 0, 768894480, 0, 0, 0)] = []
示例#7
0
    def validate_transaction(self, transaction: DDosTransaction):
        if not str(transaction.sender) in self.tree:
            print_debug_info('Sender could not be found in invited clients.')
            return False

        if not self.valid_operation(transaction):
            return False

        try:
            verify_key = nacl.signing.VerifyKey(
                transaction.sender, encoder=nacl.encoding.HexEncoder)
            transaction_hash = verify_key.verify(
                transaction.signature).decode()
            hash_str = (str(transaction.sender) + str(transaction.data) +
                        str(transaction.timestamp))
            validate_hash = self.hash(hash_str)

            if validate_hash == transaction_hash:
                print_debug_info('Signature OK')
                return True
            print_debug_info('Wrong Hash')
            return False

        except BadSignatureError:
            print_debug_info('Bad Signature, Validation Failed')
            return False
示例#8
0
def parse_args(argv):
    """ Parse command line arguments

    Args:
        argv: Arguments from the command line.
    """
    keystore_filename = 'keystore'
    port = 6666
    signing_key = None
    dns = False
    try:
        opts, _ = getopt.getopt(
            argv[1:], 'hdnp=k=s=',
            ['help', 'debug', 'dns', 'port=', 'key=', 'store='])
        for o, a in opts:
            if o in ('-h', '--help'):
                print('-d/--debug to enable debug prints')
                print('-n/--dns to start a dns chain')
                print('-p/--port to change default port')
                print('-k/--key to load a private key from a file')
                print('-s/--store to load a keystore from a file')
                sys.exit()
            if o in ('-d', '--debug'):
                set_debug()
            if o in ('-n', '--dns'):
                dns = True
            if o in ('-p', '--port'):
                try:
                    port = int(a)
                except ValueError:
                    print("Port was invalid (e.g. not an int)")
            elif o in ('-k', '--key'):
                try:
                    signing_key = load_key(filename=a)
                    print('Key successfully loaded')
                except Exception as e:
                    print('Could not load Key / Key is invalid')
                    print(e)
            elif o in ('-s', '--store'):
                keystore_filename = a
                print_debug_info(keystore_filename)

    except getopt.GetoptError as err:
        print('for help use --help')
        print(err)
        sys.exit()

    return keystore_filename, port, signing_key, dns
示例#9
0
    def resolve_conflict(self, new_chain: List[Header]):
        """ Resolves any conflicts that occur with different/outdated chains.

        Conflicts are resolved by accepting the longest valid chain.

        Args:
            new_chain: The chain to be validated,
                received from other nodes in the network.
        """
        print_debug_info('Resolving conflict')
        if len(self.chain) < len(new_chain):
            if len(self.new_chain) < len(new_chain):
                # Validate new_chain
                old_header = new_chain[0]
                for header in new_chain[1:]:
                    if self.validate_header(header, old_header):
                        old_header = header
                    else:
                        print_debug_info('Conflict resolved (old chain)')
                        return

                # Clear intermediate transactions
                self.intermediate_transactions.clear()

                # Create blockchain from new_chain
                new_bchain: OrderedDict[Header, List[Transaction]] = \
                    OrderedDict([(h, None) for h in new_chain])

                # Add known blocks
                for h, t in self.chain.items():
                    if h in new_bchain:
                        new_bchain[h] = t
                    else:
                        # Update intermediate transactions
                        self.intermediate_transactions += t

                for h, t in self.new_chain.items():
                    if h in new_bchain:
                        new_bchain[h] = t
                        if t:
                            for i_t in t:
                                try:
                                    # Remove processed transactions
                                    self.intermediate_transactions.remove(i_t)
                                except ValueError:
                                    pass

                self.new_chain = new_bchain
                print_debug_info('Conflict (Header) resolved (new chain)')

                # Ask for missing blocks
                for h, t in self.new_chain.items():
                    if t is None:
                        self.send_queue.put(('get_block', h, 'broadcast'))

        else:
            print_debug_info('Conflict resolved (old chain)')
示例#10
0
    def validate_transaction(self,
                             transaction: Transaction,
                             new_chain: bool,
                             mining: bool = False) -> bool:
        """ Validates a single transaction.

        Validates the signature, the signed hash of the signature
        and checks if the transaction amount is covered by the users balance.

        Args:
            transaction: The transaction to be validated.
            new_chain: Validate transaction for new chain?
            mining: If False, the function invalidates transaction,
                        that are already in the transaction pool.
                    If True, the function checks all transactions in the block
                        being mined.

        Returns:
            The validity (True/False) of the transaction.
        """
        if not transaction.amount > 0:
            print_debug_info('Received transaction with amount' +
                             f' {transaction.amount} lower or equal to zero')

            return False
        if transaction in self.transaction_pool and not mining:
            return False
        # if transaction.sender == '0' and transaction.signature == '0':
        #    return True
        try:
            verify_key = nacl.signing.VerifyKey(
                transaction.sender, encoder=nacl.encoding.HexEncoder)
            transaction_hash = verify_key.verify(
                transaction.signature).decode()
            validate_hash = hashlib.sha256(
                (str(transaction.sender) + str(transaction.recipient) +
                 str(transaction.amount) + str(transaction.fee) +
                 str(transaction.timestamp)).encode()).hexdigest()

            if validate_hash == transaction_hash:
                print_debug_info('Signature OK')
                return self.validate_balance(transaction)
            print_debug_info('Wrong Hash')
            return False

        except BadSignatureError:
            print_debug_info('Bad Signature, Validation Failed')
            return False
示例#11
0
    def new_transaction(self, transaction: Transaction):
        """ Add a new transaction to the blockchain.

        Args:
            transaction: Transaction that should be added.
        """
        # Make sure, only one mining reward is granted per block
        for pool_transaction in self.transaction_pool:
            if pool_transaction.sender == '0' and \
                    pool_transaction.signature == '0':
                print_debug_info(
                    'This block already granted a mining transaction!')
                return
        if transaction in self.latest_block().transactions:
            return
        if self.validate_transaction(transaction, False):
            self.transaction_pool.append(transaction)
            self.send_queue.put(('new_transaction', transaction, 'broadcast'))
            if self.gui_ready:
                self.gui_queue.put(('new_transaction', transaction, 'local'))
            self.check_auction(transaction)
        else:
            print_debug_info('Invalid transaction')
示例#12
0
    def new_block(self, block: Block):
        """ Adds a provided block to the chain after checking it for validity.

        Args:
            block: The block to be added to the chain.
        """

        # Check current chain

        if block.header.index == self.latest_block().header.index + 1:
            if self.validate_block(block, self.latest_block(), False):
                # remove transactions in new block from own transaction pool
                for block_transaction in block.transactions:
                    if block_transaction in self.transaction_pool:
                        self.transaction_pool.remove(block_transaction)
                self.send_queue.put(('new_header', block.header, 'broadcast'))
                self.chain[block.header] = block.transactions
                if self.gui_ready:
                    self.gui_queue.put(('new_block', block, 'local'))
            else:
                print_debug_info('Block not for current chain')

        self.check_new_chain(block)
示例#13
0
 def validate_balance(self, transaction: Transaction):
     balance = self.check_balance(transaction.sender, transaction.timestamp)
     if balance >= transaction.amount + transaction.fee:
         print_debug_info('Balance sufficient, transaction is valid')
         return True
     print_debug_info('Balance insufficient, transaction is invalid')
     print_debug_info(f'Transaction at fault: {transaction} ' +
                      f'was not covered by balance: {balance}')
     return False
示例#14
0
    def validate_header(self, header: Header, last_header: Header) -> bool:
        """ Validates a block-header.

        Args:
            header: Header that should be validated
            last_header: Header of current last block.
        """

        # check if previous block == last_block
        if header.previous_hash != last_header.root_hash:
            return False

        # check order of time
        if header.timestamp < last_header.timestamp:
            return False

        # Check that version of the block can be processed
        if header.version > self.version:
            print_debug_info(f'Received block with version {header.version},' +
                             ' but your current version is {self.version}.\n' +
                             'Check if there is a newer version available.')
            return False

        return True
示例#15
0
def worker(send_queue: Queue,
           receive_queue: Queue,
           command_queue: Queue,
           gui_queue: Queue,
           port: int = 6666):
    """ Takes care of the communication between nodes.

    Args:
        send_queue: Queue for messages to other nodes.
        receive_queue: Queue for messages to the attached blockchain.
    """
    print_debug_info("Started networking")
    # Example:
    # Find peers
    # Main loop:
    # - check send_queue (send new messages)
    # - check incoming messages
    #   -- networking message (e.g. new peer, get peers)
    #   -- Blockchain message: put on receive_queue

    SERVER.setup(port)
    PEERS.setup(send_queue, gui_queue, port)

    example_worker(send_queue, receive_queue, command_queue)
示例#16
0
    def new_header(self, header: Header):
        """ Check if new header is valid and ask for the corresponding block

        Args:
            header: New block-header
        """

        if header.index > self.latest_header().index + 1:
            # block higher then current chain:
            # resolve conflict between chains
            self.send_queue.put(('get_chain', '', 'broadcast'))
            print_debug_info('Chain out-of-date.')
            print_debug_info('Updating...')
            return

        if self.validate_header(header, self.latest_header()):
            self.send_queue.put(('get_block', header, 'broadcast'))
            print_debug_info('Valid header, asked for full block')
        else:
            print_debug_info('Invalid header')
示例#17
0
def blockchain_loop(blockchain: Blockchain, processor):
    """ The main loop of the blockchain thread.

    Receives messages and processes them.

    Args:
        blockchain: The blockchain upon which to operate.
        processor: Processor used to handle blockchain messages.
    """
    while True:
        msg_type, msg_data, msg_address = receive_queue.get()
        print_debug_info('Processing: ' + msg_type)
        try:
            receive_msg(msg_type, msg_data, msg_address, blockchain, processor)
        except AssertionError as e:
            print_debug_info(f'Assertion Error on message\
                 {msg_type}:{msg_data}:{msg_address}')
            print_debug_info(e)
示例#18
0
    def new_transaction(self, transaction: DDosTransaction):
        if not self.validate_transaction(transaction):
            print_debug_info('Invalid transaction')
            return

        for pool_transaction in self.transaction_pool:
            if transaction == pool_transaction:
                print_debug_info('Transaction already in pool')
                return
            if transaction.data == pool_transaction.data:
                print_debug_info('This operation is already in the pool')
                return

        self.transaction_pool.append(transaction)
        self.send_queue.put(('new_transaction', transaction, 'broadcast'))
        if self.gui_ready:
            self.gui_queue.put(('new_transaction', transaction, 'local'))
        if len(self.transaction_pool) >= 5:
            self.create_m_blocks()
示例#19
0
    def validate_transaction(self, transaction: DNS_Transaction,
                             new_chain: bool,
                             mining: bool = False) -> bool:
        """ Validates a given transaction.
            Overwrites validate_transaction from pow_chain.py.
            Checks if a transaction is
            1.) either a resolved auction or bid, and thus previously validated
            2.) a valid domain operation (register, update, transfer, bid) or
            3.) a valid 'normal' transaction
        """
        normal_transaction = False
        valid_domain_operation = False

        # Resolved auctions, original auction as well as all bids are verified when first posted
        # Therefore we can simply accept these transactions
        if transaction.sender == '0' and transaction.signature == '1':
            return True

        if transaction.data.type == '':
            normal_transaction = True
            if transaction.amount == 0:
                return False
        if not normal_transaction:
            valid_domain_operation = self._is_valid_domain_transaction(
                transaction, new_chain)
            if not valid_domain_operation:
                return False

        if not mining:
            found = False
            for t in self.transaction_pool:
                if t == transaction:
                    return False
                if normal_transaction:
                    continue
                if t.data.domain_name == transaction.data.domain_name:
                    found = True
                    if transaction.data.type == 'r':
                        return False
                    if transaction.data.type == 'u' and t.sender != transaction.sender:
                        return False
            if not found and transaction.data.type == 'u' and not valid_domain_operation:
                return False

        try:
            verify_key = nacl.signing.VerifyKey(
                transaction.sender, encoder=nacl.encoding.HexEncoder)
            transaction_hash = verify_key.verify(
                transaction.signature).decode()
            validate_hash = hashlib.sha256(
                (str(transaction.sender) + str(transaction.recipient) +
                 str(transaction.amount) + str(transaction.fee) +
                 str(transaction.timestamp) + str(transaction.data)).encode()
            ).hexdigest()

            if validate_hash == transaction_hash:
                print_debug_info('Signature OK')
                return self.validate_balance(transaction)
            print_debug_info('Wrong Hash')
            return False

        except BadSignatureError:
            print_debug_info('Bad Signature, Validation Failed')
            return False
示例#20
0
 def valid_operation(self, transaction: DDosTransaction):
     # Invite
     if transaction.data.type == 'i':
         if str(transaction.data.data) in self.tree:
             print_debug_info('Client is already invited!')
             return False
     # Uninvite / Purge
     elif transaction.data.type == 'ui' or transaction.data.type == 'p':
         if str(transaction.data.data) not in self.tree:
             print_debug_info('Client could not be found!')
             return False
         node_to_remove = self.tree.get_node_by_content(
             str(transaction.data.data))
         sender_node = self.tree.get_node_by_content(str(
             transaction.sender))
         if node_to_remove.content not in sender_node:
             print_debug_info('No permission to delete this node!')
             return False
     # Block IP-Address
     elif transaction.data.type == 'b':
         if transaction.data.data in self.blocked_ips:
             ancestors = self.blocked_ips[transaction.data.data].\
                 get_ancestors()
             if not str(
                     transaction.sender) in [a.content for a in ancestors]:
                 print_debug_info('IP was already blocked')
                 return False
     # Unblock IP-Address
     elif transaction.data.type == 'ub':
         if transaction.data.data in self.blocked_ips:
             if str(transaction.sender) ==\
                     self.blocked_ips[transaction.data.data].content:
                 return True
             ancestors = self.blocked_ips[transaction.data.data].\
                 get_ancestors()
             if str(transaction.sender) in [a.content for a in ancestors]:
                 # IP blocked from descendant
                 return True
             print_debug_info('IP was already blocked')
             return False
         else:
             print_debug_info('Trying to unblock IP that was not blocked')
             return False
     return True